diff --git a/.ci_fedora.sh b/.ci_fedora.sh index 359516ab33..c865e8a0aa 100755 --- a/.ci_fedora.sh +++ b/.ci_fedora.sh @@ -21,11 +21,10 @@ then cmd="sudo docker" fi test . != ".$2" && mpi="$2" || mpi=openmpi - test . != ".$3" && version="$3" || version=rawhide - time $cmd pull registry.fedoraproject.org/fedora:$version + time $cmd pull ghcr.io/boutproject/bout-container-base:main time $cmd create --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ --shm-size 256M \ - --name mobydick registry.fedoraproject.org/fedora:$version \ + --name mobydick ghcr.io/boutproject/bout-container-base:main \ /tmp/BOUT-dev/.ci_fedora.sh $mpi time $cmd cp ${TRAVIS_BUILD_DIR:-$(pwd)} mobydick:/tmp/BOUT-dev time $cmd start -a mobydick @@ -34,27 +33,8 @@ fi test . != ".$1" && mpi="$1" || mpi=openmpi -## If we are called as root, setup everything -if [ $UID -eq 0 ] -then - cat /etc/os-release - # Ignore weak depencies - echo "install_weak_deps=False" >> /etc/dnf/dnf.conf - echo "minrate=10M" >> /etc/dnf/dnf.conf - export FORCE_COLUMNS=200 - time dnf -y install dnf5 - time dnf5 -y install dnf5-plugins cmake python3-zoidberg python3-natsort - # Allow to override packages - see #2073 - time dnf5 copr enable -y davidsch/fixes4bout || : - time dnf5 -y upgrade - time dnf5 -y builddep bout++ - useradd test - cp -a /tmp/BOUT-dev /home/test/ - chown -R test /home/test - chmod u+rwX /home/test -R - su - test -c "${0/\/tmp/\/home\/test} $mpi" ## If we are called as normal user, run test -else + cp -a /tmp/BOUT-dev /home/test/ . /etc/profile.d/modules.sh module load mpi/${1}-x86_64 export OMPI_MCA_rmaps_base_oversubscribe=yes @@ -67,13 +47,15 @@ else export OMP_NUM_THREADS=1 cd cd BOUT-dev + python3 -m ensurepip + python3 -m pip install -r requirements.txt echo "starting configure" time cmake -S . -B build -DBOUT_USE_PETSC=ON \ -DBOUT_UPDATE_GIT_SUBMODULE=OFF \ -DBOUT_USE_SYSTEM_FMT=ON \ -DBOUT_USE_SYSTEM_MPARK_VARIANT=ON \ - -DBOUT_USE_SUNDIALS=ON + -DBOUT_USE_SUNDIALS=ON \ + -DBOUT_USE_SYSTEM_CPPTRACE=ON time make -C build build-check -j 2 time make -C build check -fi diff --git a/.clang-format b/.clang-format index a80c59bddd..51cf39683a 100644 --- a/.clang-format +++ b/.clang-format @@ -93,6 +93,7 @@ PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left +QualifierAlignment: Left ReflowComments: false SortIncludes: true # SortUsingDeclarations: true diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 0000000000..58566f02b1 --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1,2 @@ +# ignore matlab files +**/*.m diff --git a/.clang-tidy b/.clang-tidy index 556b8738f8..6fc7fd19d9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: 'clang-diagnostic-*,clang-analyzer-*,performance-*,readability-*,bugprone-*,clang-analyzer-*,cppcoreguidelines-*,mpi-*,misc-*,-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers,-misc-non-private-member-variables-in-classes,-clang-analyzer-optin.mpi*,-bugprone-exception-escape,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-readability-function-cognitive-complexity,-misc-no-recursion,-bugprone-easily-swappable-parameters,-readability-identifier-length' +Checks: 'clang-diagnostic-*,clang-analyzer-*,performance-*,readability-*,bugprone-*,clang-analyzer-*,cppcoreguidelines-*,mpi-*,misc-*,-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers,-misc-non-private-member-variables-in-classes,-clang-analyzer-optin.mpi*,-bugprone-exception-escape,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-readability-function-cognitive-complexity,-misc-no-recursion,-bugprone-easily-swappable-parameters,-readability-identifier-length,-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access' WarningsAsErrors: '' HeaderFilterRegex: '' FormatStyle: file @@ -9,6 +9,18 @@ CheckOptions: # otherwise this breaks `ASSERT` macros! - key: readability-simplify-boolean-expr.IgnoreMacros value: 'true' + + - key: misc-include-cleaner.IgnoreHeaders + value: 'petsc.*\.h;mpi\.h' + + - key: readability-qualified-auto.IgnoreAliasing + value: 'false' + + - key: readability-qualified-auto.AllowedTypes + value: 'MPI_Comm' + + - key: misc-include-cleaner.IgnoreHeaders + value: 'adios2/.*;bits/.*;cpptrace/.*;petsc.*\.h' --- Disabled checks and reasons: @@ -17,6 +29,7 @@ These are all basically unavoidable in HPC numeric code: -readability-magic-numbers -cppcoreguidelines-avoid-magic-numbers -cppcoreguidelines-pro-bounds-pointer-arithmetic +-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access -readability-function-cognitive-complexity -bugprone-easily-swappable-parameters -readability-identifier-length diff --git a/.clangd b/.clangd new file mode 100644 index 0000000000..421b7e10d3 --- /dev/null +++ b/.clangd @@ -0,0 +1,6 @@ +Diagnostics: + MissingIncludes: Strict + ClangTidy: + FastCheckFilter: None + Includes: + IgnoreHeader: ["adios2/.*", "bits/.*", "cpptrace/.*", "petsc.*"] diff --git a/.docker/fedora/Dockerfile b/.docker/fedora/Dockerfile index 56445f8cea..e0351bb605 100644 --- a/.docker/fedora/Dockerfile +++ b/.docker/fedora/Dockerfile @@ -1,5 +1,5 @@ -ARG BASE -FROM ghcr.io/dschwoerer/bout-container-base:$BASE +ARG BASE=mpich-full-main +FROM ghcr.io/boutproject/bout-container-base:$BASE # ---------------------------------------------------------------- # Build and install BOUT++ @@ -22,18 +22,26 @@ RUN git clone $URL \ && git checkout $COMMIT \ && git submodule update --init --recursive - +ENV HOME=/home/boutuser WORKDIR /home/boutuser/BOUT-dev -RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/opt/bout++/ \ +RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local/ \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBOUT_GENERATE_FIELDOPS=OFF \ - -DBOUT_USE_PETSC=ON -DPETSc_ROOT=/opt/petsc \ + -DBOUT_USE_PETSC=ON -DPETSc_ROOT=/usr/local \ -DBOUT_ENABLE_PYTHON=ON \ -DBOUT_USE_SUNDIALS=ON -DSUNDIALS_ROOT=/usr/lib64/$MPI/ -DSUNDIALS_INCLUDE_DIR=/usr/include/$MPI-x86_64/sundials/ \ - $CMAKE_OPTIONS || (cat /home/boutuser/BOUT-dev/build/CMakeFiles/CMake{Output,Error}.log ; exit 1) + $CMAKE_OPTIONS || (cat /home/boutuser/BOUT-dev/build/CMakeFiles/CMake{Output,Error}.log ; exit 1); \ + make -C build -j 2 VERBOSE=1; \ + sudo make -C build install; \ + rm -rf build +# Add unversioned path for python +RUN sudo ln -s /usr/local/lib/python3.* /usr/local/lib/python3.x -RUN make -C build -j 2 -RUN sudo make -C build install +ENV PATH=/usr/local/bin:$PATH \ + LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH \ + PYTHONPATH=/usr/local/lib/python3.x/site-packages/:$PYTHONPATH -RUN find /opt/bout++ +# smoke test +RUN python3 -c 'import boutpp' diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 4990f2586e..945f769e70 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,22 @@ # Clang-format whole repo d8f14fdddb5ca0fbb32d8e2bf5ac2960d6ac5ce6 ed2117e6d6826a98b6988e2f18c0c34e408563b6 +# CMake formatting +17ac13c28aa3b34a0e46dbe87bb3874f6b25e706 +# Added by the bot +4b010b7634aee1045743be80c268d4644522cd29 +52301380586fdbf890f620c04f689b08d89a6c34 +a71cad2dd6ace5741a754e2ca7daacd4bb094e0e +83cf77923a4c72e44303354923021acf932b4fd2 +2c2402ed59c91164eaff46dee0f79386b7347e9e +05b7c571544c3bcb153fce67d12b9ac48947fc2d +c8f38049359170a34c915e209276238ea66b9a1e +65880e4af16cb95438437bf057c4b9fdb427b142 +d1cfb8abd6aa5c76e6c1a4d7ab20929c65f8afc2 +8d5cb39e03c2644715a50684f8cd0318b4e82676 +ec69e8838be2dde140a915e50506f8e1ce3cb534 +f2bc0488a298f136294c523bc5ab4086d090059b +c59626a0498b0cfd1be46f84f0fa38b4855a43cc +1b4707187a3a85126338303dc766280b8fb2dc56 +b2e2f3575e68f771be2a4341af5fd14e2870469e +64e4285ec38569f66d31e589a4610cefd16d8076 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c6b0738bd2..e121ea49e4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,11 +6,6 @@ updates: interval: "weekly" directory: "/" - - package-ecosystem: "pip" - schedule: - interval: "daily" - directory: "/" - - package-ecosystem: "github-actions" schedule: interval: "weekly" diff --git a/.github/workflows/auto-formatting.yml b/.github/workflows/auto-formatting.yml new file mode 100644 index 0000000000..518cfd3898 --- /dev/null +++ b/.github/workflows/auto-formatting.yml @@ -0,0 +1,33 @@ +name: format-command + +on: + pull_request: + +jobs: + clang-format: + # Release candidate branches tend to have big PRs which causes all sorts of problems + if: ${{ !endsWith(github.head_ref, '-rc') }} + runs-on: ubuntu-latest + steps: + # Checkout the pull request branch, also include all history + - uses: actions/checkout@v7 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + + - name: "Set up Python" + uses: actions/setup-python@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Install dev tools + run: uv sync --only-dev + + - name: Check prek versions are up-to-date + run: uv tool run sync-with-uv --check --diff + + - name: Run everything + run: uv tool run prek run --show-diff-on-failure --from-ref origin/${{ github.base_ref }} diff --git a/.github/workflows/black-fix.yml b/.github/workflows/black-fix.yml deleted file mode 100644 index 92866dfcc2..0000000000 --- a/.github/workflows/black-fix.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: black - -on: - push: - paths: - - '**.py' - - 'bin/**' - - '**/runtest' - - '**/run' - - 'tests/integrated/test_suite' - - 'tests/requirements/*' - - 'tools/tokamak_grids/**' - -defaults: - run: - shell: bash - -jobs: - black: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - - name: Install black - run: | - sudo apt update -y - sudo apt -y install python3-pip python3-setuptools python3-wheel - pip3 install black - - - name: Version - run: | - python3 --version - $HOME/.local/bin/black --version - - - name: Run black - run: | - pwd - ls - $HOME/.local/bin/black tests/ tools/ $(grep -EIlr '^#!.*python.*$' bin/ tests/ tools/ src/ | grep -v _boutpp_build) - - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: "Apply black changes" diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml deleted file mode 100644 index 87f1947802..0000000000 --- a/.github/workflows/clang-format.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: clang-format-command - -on: - pull_request: - paths: - - '**.cxx' - - '**.hxx' - -jobs: - clang-format: - runs-on: ubuntu-latest - steps: - # Checkout the pull request branch, also include all history - - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - fetch-depth: 0 - - # This also installs git-clang-format - - name: Install clang-format - run: sudo apt install clang-format - - - name: Run clang-format - id: format - run: git clang-format origin/${{ github.base_ref }} - - - name: Commit to the PR branch - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: "Apply clang-format changes" diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 1e2f88d208..0573addd9c 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -5,9 +5,6 @@ on: paths: - '**.cxx' - '**.hxx' - branches-ignore: - # Release candidate branches tend to have big PRs which causes all sorts of problems - - 'v*rc' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -15,14 +12,16 @@ concurrency: jobs: review: + # Release candidate branches tend to have big PRs which causes all sorts of problems + if: ${{ !endsWith(github.head_ref, '-rc') }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: submodules: true - name: Run clang-tidy - uses: ZedThree/clang-tidy-review@v0.20.1 + uses: ZedThree/clang-tidy-review@v0.23.1 id: review with: build_dir: build @@ -47,4 +46,4 @@ jobs: -DBOUT_UPDATE_GIT_SUBMODULE=OFF - name: Upload clang-tidy fixes - uses: ZedThree/clang-tidy-review/upload@v0.20.1 + uses: ZedThree/clang-tidy-review/upload@v0.23.1 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a1dac41a6b..b8d5fc8a70 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -6,7 +6,7 @@ on: - master - next # Add your branch here if you want containers for it - - db-WIP + - fix3121 - docker-ci env: @@ -26,18 +26,18 @@ jobs: matrix: mpi: [mpich] metric3d: - - name: "With OpenMP" + - name: "With 3D Metrics" cmake: ON - base_prefix: "openmp-" + base_prefix: "" tag_prefix: "3d-" - - name: "Without OpenMP" + - name: "With 2D Metrics" cmake: OFF base_prefix: "" tag_prefix: "" config: - name: "Debug" tag_postfix: "debug" - options: "-DCHECK=3" + options: "-DCHECK=4" base_postfix: "mini" - name: "Optimised" @@ -47,12 +47,12 @@ jobs: - name: "Full" tag_postfix: "full" - options: "-DCHECK=3" + options: "-DCHECK=4" base_postfix: "full" steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Log in to the Container registry uses: docker/login-action@master @@ -75,7 +75,7 @@ jobs: build-args: | BASE=${{ matrix.mpi }}-${{ matrix.metric3d.base_prefix }}${{ matrix.config.base_postfix }}-main MPI=${{ matrix.mpi }} - CMAKE_OPTIONS=${{ matrix.config.options }} -DBOUT_ENABLE_METRIC_3D=${{ matrix.metric3d.cmake }} -DBOUT_ENABLE_OPENMP=${{ matrix.metric3d.cmake }} + CMAKE_OPTIONS=${{ matrix.config.options }} -DBOUT_ENABLE_METRIC_3D=${{ matrix.metric3d.cmake }} COMMIT=${{ github.sha }} URL=${{ github.server_url }}/${{ github.repository }} context: .docker/fedora/ diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index ea68e58c12..e2023fa5c8 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -17,12 +17,12 @@ jobs: if: always() steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 submodules: true - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 - name: Install dependencies run: python -m pip install --upgrade pip && pip install --upgrade build && @@ -57,12 +57,12 @@ jobs: if: always() steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 submodules: true - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 - name: Install dependencies run: python -m pip install --upgrade pip && pip install --upgrade build && @@ -106,12 +106,12 @@ jobs: if: always() steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 submodules: true - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 - name: Install dependencies run: python -m pip install --upgrade pip && pip install --upgrade build && diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 90c63e0233..d9cbc07574 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,6 +30,7 @@ jobs: OMPI_MCA_rmaps_base_oversubscribe: yes PRTE_MCA_rmaps_default_mapping_policy: ":oversubscribe" MPIRUN: mpiexec -np + BOUT_TEST_DOWNLOAD_FLAGS: --retry-on-http-error=502,503,504 --tries 3 strategy: fail-fast: true matrix: @@ -39,7 +40,7 @@ jobs: is_cron: - ${{ github.event_name == 'cron' }} config: - - name: "CMake, PETSc unreleased, ADIOS2" + - name: "PETSc unreleased, ADIOS2, 3D metrics" os: ubuntu-24.04 cmake_options: "-DBUILD_SHARED_LIBS=ON -DBOUT_ENABLE_METRIC_3D=ON @@ -120,22 +121,6 @@ jobs: omp_num_threads: 2 on_cron: false - - name: "CMake, new PETSc" - os: ubuntu-latest - cmake_options: "-DBUILD_SHARED_LIBS=ON - -DBOUT_ENABLE_METRIC_3D=ON - -DBOUT_ENABLE_OPENMP=ON - -DBOUT_USE_PETSC=ON - -DBOUT_USE_SLEPC=ON - -DBOUT_USE_SUNDIALS=ON - -DBOUT_USE_HYPRE=OFF - -DBOUT_ENABLE_PYTHON=ON - -DSUNDIALS_ROOT=/home/runner/local - -DPETSC_DIR=/home/runner/local/petsc - -DSLEPC_DIR=/home/runner/local/slepc" - build_petsc: -petsc - on_cron: false - exclude: - is_cron: true config: @@ -168,21 +153,29 @@ jobs: libparpack2-dev libhypre-dev - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: submodules: true - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.x' + cache: 'pip' - name: Install pip packages run: | python -m pip install --upgrade pip setuptools python -m pip install -r requirements.txt + - name: Cache Zenodo test data + uses: actions/cache@v6 + with: + path: build/tests/integrated/test-fci-mpi/grid.fci.nc + # If we update the test, invalidate the cache + key: zenodo-data-${{ hashFiles('tests/integrated/test-fci-mpi/CMakeLists.txt') }} + - name: Cache SUNDIALS build - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: /home/runner/local key: bout-sundials-${{ matrix.config.os }}${{ matrix.config.build_petsc }} @@ -194,7 +187,7 @@ jobs: run: BUILD_PETSC=${{ matrix.config.build_petsc }} ./.build_petsc_for_ci.sh ${{ matrix.config.build_petsc_branch }} - name: Build ADIOS2 - run: BUILD_ADIOS2=${{ matrix.config.build_adios2 }} ./.build_adios2_for_ci.sh + run: BUILD_ADIOS2=${{ matrix.config.build_adios2 }} ./.build_adios2_for_ci.sh - name: Build BOUT++ run: UNIT_ONLY=${{ matrix.config.unit_only }} ./.ci_with_cmake.sh ${{ matrix.config.cmake_options }} @@ -204,39 +197,22 @@ jobs: # standard_tests timeout-minutes: 120 runs-on: ubuntu-latest + env: + BOUT_TEST_DOWNLOAD_FLAGS: --retry-on-http-error=502,503,504 --tries 3 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: submodules: true - - name: Build Fedora rawhide - run: ./.ci_fedora.sh setup openmpi rawhide + + - name: Cache Zenodo test data + uses: actions/cache@v6 + with: + path: build/tests/integrated/test-fci-mpi/grid.fci.nc + # If we update the test, invalidate the cache + key: zenodo-data-${{ hashFiles('tests/integrated/test-fci-mpi/CMakeLists.txt') }} + + - name: Build Fedora + run: ./.ci_fedora.sh setup openmpi shell: bash env: TRAVIS_BUILD_DIR: ${{ github.workspace }} - CUDA: - timeout-minutes: 60 - runs-on: ubuntu-latest - container: ghcr.io/ggeorgakoudis/boutdev-cuda:latest - - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - name: Build minimal CUDA 12.2 @ GCC9.4.0 @ Ubuntu 20.04 - run: | - . /spack/share/spack/setup-env.sh - spack env activate -p /spack-env - git config --global --add safe.directory $GITHUB_WORKSPACE - rm -rf build - cmake -S $GITHUB_WORKSPACE -B build \ - -DCMAKE_C_COMPILER=gcc \ - -DCMAKE_CXX_COMPILER=g++ \ - -DBOUT_ENABLE_RAJA=on \ - -DBOUT_ENABLE_UMPIRE=on \ - -DBOUT_ENABLE_CUDA=on \ - -DCMAKE_CUDA_ARCHITECTURES=80 \ - -DCUDA_ARCH=compute_80,code=sm_80 \ - -DBOUT_ENABLE_WARNINGS=off \ - -DBOUT_USE_SYSTEM_FMT=on - cd build - make -j 4 diff --git a/.gitignore b/.gitignore index 934da1c0de..164a88925c 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,4 @@ coverage/ /BOUT++-v*.tar.xz /CMakeCache.txt /CMakeFiles/cmake.check_cache +.vscode/ diff --git a/.gitmodules b/.gitmodules index 4b33b3b615..59fb05ab50 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "externalpackages/boutdata"] path = externalpackages/boutdata url = https://github.com/boutproject/boutdata.git +[submodule "externalpackages/cpptrace"] + path = externalpackages/cpptrace + url = https://github.com/jeremy-rifkin/cpptrace.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..84a440847b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,62 @@ +# This file requires prek rather than pre-commit +exclude: + glob: + - "tools/**/*.pro" + - "tools/**/*.m" + - "tools/**/*.[ch]" + +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: builtin + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-added-large-files + +# This would be nice to use, but it is a significant slowdown: + +# # Sync versions of hook tools with `uv.lock` +# - repo: https://github.com/tsvikas/sync-with-uv +# rev: v0.5.0 +# hooks: +# - id: sync-with-uv + +# C++ formatting +- repo: https://github.com/pre-commit/mirrors-clang-format + rev: v22.1.1 + hooks: + - id: clang-format + types_or: [c++, c, cuda] + +# Python linting and formatting +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.6 + hooks: + # Run the linter. + - id: ruff-check + # Run the formatter. + - id: ruff-format + +# CMake formatting +- repo: https://github.com/cheshirekow/cmake-format-precommit + rev: v0.6.13 + hooks: + - id: cmake-format + additional_dependencies: [pyyaml>=5.1] + +# Various config files +- repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.37.0 + hooks: + - id: check-github-workflows + - id: check-readthedocs + - id: check-citation-file-format + - id: check-dependabot + +# Docs linting +- repo: https://github.com/sphinx-contrib/sphinx-lint + rev: v1.0.2 + hooks: + - id: sphinx-lint + files: 'manual/sphinx/.*' diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d346d47e8..41e3e2c47b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog -## [v6.0.0](https://github.com/boutproject/BOUT-dev/tree/next) -[Full Changelog](https://github.com/boutproject/BOUT-dev/compare/v5.1.0...next) +## [v5.2.0](https://github.com/boutproject/BOUT-dev/tree/v5.2.0 + +[Full Changelog](https://github.com/boutproject/BOUT-dev/compare/v5.1.1...v5.2.0) ### Breaking changes @@ -16,6 +17,67 @@ confusing. Now, monitors are called with the current number of completed monitor-steps. +## Changes + +- Fixes for FreeBSD [\#3172][https://github.com/boutproject/BOUT-dev/pull/3172] ([tomc271][https://github.com/tomc271]) +- snes: Print a warning if the coloring is non-symmetric. [\#3159][https://github.com/boutproject/BOUT-dev/pull/3159] ([bendudson][https://github.com/bendudson]) +- Cmake fix from master [\#3152][https://github.com/boutproject/BOUT-dev/pull/3152] ([oparry-ukaea][https://github.com/oparry-ukaea]) +- Bump ZedThree/clang-tidy-review from 0.20.1 to 0.21.0 [\#3150][https://github.com/boutproject/BOUT-dev/pull/3150] ([ZedThree][https://github.com/ZedThree]) +- SNES solver: Added a PID controller to update the timestep [\#3146][https://github.com/boutproject/BOUT-dev/pull/3146] ([malamast][https://github.com/malamast]) +- Fix name clash in some examples and MMS tests [\#3145][https://github.com/boutproject/BOUT-dev/pull/3145] ([ZedThree][https://github.com/ZedThree]) +- Handle absolute paths to options file [\#3142][https://github.com/boutproject/BOUT-dev/pull/3142] ([bendudson][https://github.com/bendudson]) +- Revert eliminate hypre boundary equations [\#3136][https://github.com/boutproject/BOUT-dev/pull/3136] ([ZedThree][https://github.com/ZedThree]) +- CI: Switch to released fedora [\#3134][https://github.com/boutproject/BOUT-dev/pull/3134] ([dschwoerer][https://github.com/dschwoerer]) +- Generalise FakeMeshFixture to allow configurable grid spacing via templating [\#3132][https://github.com/boutproject/BOUT-dev/pull/3132] ([tomc271][https://github.com/tomc271]) +- Fixes for ELM-PB preconditioner [\#3128][https://github.com/boutproject/BOUT-dev/pull/3128] ([Steven-Roberts][https://github.com/Steven-Roberts]) +- Fix cast for new petsc [\#3117][https://github.com/boutproject/BOUT-dev/pull/3117] ([dschwoerer][https://github.com/dschwoerer]) +- Fix bug where ARKODE considered all problems linear [\#3114][https://github.com/boutproject/BOUT-dev/pull/3114] ([Steven-Roberts][https://github.com/Steven-Roberts]) +- cvode: Add linear_solver option [\#3112][https://github.com/boutproject/BOUT-dev/pull/3112] ([bendudson][https://github.com/bendudson]) +- periodicX communication fixes [\#3109][https://github.com/boutproject/BOUT-dev/pull/3109] ([bendudson][https://github.com/bendudson]) +- beuler solver timestep and Jacobian calculation [\#3107][https://github.com/boutproject/BOUT-dev/pull/3107] ([bendudson][https://github.com/bendudson]) +- Feature/snes stencil [\#3104][https://github.com/boutproject/BOUT-dev/pull/3104] ([seimtpow][https://github.com/seimtpow]) +- Add attributes to ADIOS2 output to "define" dimensions as names. We n… [\#3098][https://github.com/boutproject/BOUT-dev/pull/3098] ([pnorbert][https://github.com/pnorbert]) +- CI: Avoid issues with special characters [\#3090][https://github.com/boutproject/BOUT-dev/pull/3090] ([dschwoerer][https://github.com/dschwoerer]) +- CI: Fix clang format [\#3086][https://github.com/boutproject/BOUT-dev/pull/3086] ([dschwoerer][https://github.com/dschwoerer]) +- Small updates for FCI output [\#3085][https://github.com/boutproject/BOUT-dev/pull/3085] ([bendudson][https://github.com/bendudson]) +- Eliminate boundary equations to improve HYPRE solves [\#3082][https://github.com/boutproject/BOUT-dev/pull/3082] ([rfalgout][https://github.com/rfalgout]) +- Elm-pb example with relaxing phi [\#3081][https://github.com/boutproject/BOUT-dev/pull/3081] ([bendudson][https://github.com/bendudson]) +- CI: Show more output of dnf5 to help debugging [\#3071][https://github.com/boutproject/BOUT-dev/pull/3071] ([dschwoerer][https://github.com/dschwoerer]) +- Fix clang tidy review [\#3070][https://github.com/boutproject/BOUT-dev/pull/3070] ([ZedThree][https://github.com/ZedThree]) +- CMake: Bump downloaded SUNDIALS version [\#3067][https://github.com/boutproject/BOUT-dev/pull/3067] ([ZedThree][https://github.com/ZedThree]) +- Use isfinite, fix pvode linking [\#3063][https://github.com/boutproject/BOUT-dev/pull/3063] ([bendudson][https://github.com/bendudson]) +- Make iteration more robust and give more options in LaplaceNaulin [\#3061][https://github.com/boutproject/BOUT-dev/pull/3061] ([johnomotani][https://github.com/johnomotani]) +- Fixes zoidberg [\#3054][https://github.com/boutproject/BOUT-dev/pull/3054] ([dschwoerer][https://github.com/dschwoerer]) +- Fix finalise in boutpp [\#3053][https://github.com/boutproject/BOUT-dev/pull/3053] ([dschwoerer][https://github.com/dschwoerer]) +- Avoid some warnings for 3D Metrics [\#3052][https://github.com/boutproject/BOUT-dev/pull/3052] ([dschwoerer][https://github.com/dschwoerer]) +- Minor fixes for the python backend [\#3051][https://github.com/boutproject/BOUT-dev/pull/3051] ([dschwoerer][https://github.com/dschwoerer]) +- Cleanup BOUT_HOST_DEVICE qualifiers [\#3040][https://github.com/boutproject/BOUT-dev/pull/3040] ([ggeorgakoudis][https://github.com/ggeorgakoudis]) +- Deprecate options that are only used as default for other options [\#3038][https://github.com/boutproject/BOUT-dev/pull/3038] ([dschwoerer][https://github.com/dschwoerer]) +- Avoid using the wrong grid by accident [\#3036][https://github.com/boutproject/BOUT-dev/pull/3036] ([dschwoerer][https://github.com/dschwoerer]) +- Use PEP 625 compatible archive name (next) [\#3035][https://github.com/boutproject/BOUT-dev/pull/3035] ([dschwoerer][https://github.com/dschwoerer]) +- CI: Increase check level for debug run + unit test fixes [\#3033][https://github.com/boutproject/BOUT-dev/pull/3033] ([dschwoerer][https://github.com/dschwoerer]) +- Avoid `#define` conflict with sundials [\#3031][https://github.com/boutproject/BOUT-dev/pull/3031] ([dschwoerer][https://github.com/dschwoerer]) +- Ensure PETSc headers are included after `bout/petsclib.hxx` [\#3024][https://github.com/boutproject/BOUT-dev/pull/3024] ([ZedThree][https://github.com/ZedThree]) +- Fix minor HYPRE and ADIOS2 compilation issues [\#3022][https://github.com/boutproject/BOUT-dev/pull/3022] ([ZedThree][https://github.com/ZedThree]) +- Fix recompilation cascade [\#3021][https://github.com/boutproject/BOUT-dev/pull/3021] ([ZedThree][https://github.com/ZedThree]) +- Move `invert3x3` out of general purpose `utils.hxx` header [\#3018][https://github.com/boutproject/BOUT-dev/pull/3018] ([ZedThree][https://github.com/ZedThree]) +- Replace deprecated `boututils.file_import` [\#3017][https://github.com/boutproject/BOUT-dev/pull/3017] ([ZedThree][https://github.com/ZedThree]) +- CI: clang-tidy-review tweaks [\#3016][https://github.com/boutproject/BOUT-dev/pull/3016] ([ZedThree][https://github.com/ZedThree]) +- Backward Euler solver improvements [\#3009][https://github.com/boutproject/BOUT-dev/pull/3009] ([bendudson][https://github.com/bendudson]) +- Fix circular header dependency: mesh.hxx <-> griddata.hxx [\#3008][https://github.com/boutproject/BOUT-dev/pull/3008] ([ZedThree][https://github.com/ZedThree]) +- Fix exception message [\#3003][https://github.com/boutproject/BOUT-dev/pull/3003] ([dschwoerer][https://github.com/dschwoerer]) +- Ensure pointer is checked before dereferencing [\#3002][https://github.com/boutproject/BOUT-dev/pull/3002] ([dschwoerer][https://github.com/dschwoerer]) +- Fix: preserve regionID [\#3000][https://github.com/boutproject/BOUT-dev/pull/3000] ([dschwoerer][https://github.com/dschwoerer]) +- Lazy grid loading [\#2991][https://github.com/boutproject/BOUT-dev/pull/2991] ([dschwoerer][https://github.com/dschwoerer]) +- Fix compilation warnings with SUNDIALS 7.1.0 [\#2990][https://github.com/boutproject/BOUT-dev/pull/2990] ([Steven-Roberts][https://github.com/Steven-Roberts]) +- Add LC gitlab CI for GPU build/run tests [\#2989][https://github.com/boutproject/BOUT-dev/pull/2989] ([ggeorgakoudis][https://github.com/ggeorgakoudis]) +- BoutMask non-const operator[](Ind3D) [\#2988][https://github.com/boutproject/BOUT-dev/pull/2988] ([bendudson][https://github.com/bendudson]) +- Use consistently signed char [\#2987][https://github.com/boutproject/BOUT-dev/pull/2987] ([dschwoerer][https://github.com/dschwoerer]) +- naulin laplace: Acceptance tolerances after maxits [\#2983][https://github.com/boutproject/BOUT-dev/pull/2983] ([bendudson][https://github.com/bendudson]) +- Merge v5.1.1 into `next` [\#2978][https://github.com/boutproject/BOUT-dev/pull/2978] ([ZedThree][https://github.com/ZedThree]) +- CI: Bump all ubuntu images [\#2977][https://github.com/boutproject/BOUT-dev/pull/2977] ([ZedThree][https://github.com/ZedThree]) +- Read 2D variables into Field3D [\#2975][https://github.com/boutproject/BOUT-dev/pull/2975] ([bendudson][https://github.com/bendudson]) + ## [v5.1.1](https://github.com/boutproject/BOUT-dev/tree/v5.1.1) [Full Changelog](https://github.com/boutproject/BOUT-dev/compare/v5.1.0...v5.1.1) diff --git a/CITATION.cff b/CITATION.cff index 81f5c35e82..318fcd1406 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -197,11 +197,27 @@ authors: - family-names: Kryjak given-names: Mike -version: 5.1.1 -date-released: 2023-04-10 + - family-names: Powell + given-names: Seimon + - family-names: Ağgül + given-names: Mustafa + - family-names: Parry + given-names: Owen + - family-names: Podhorszki + given-names: Norbert + - family-names: Falgout + given-names: Rob + - family-names: Li + given-names: Nami + - family-names: Body + given-names: Tom + - family-names: Tsagkaridis + given-names: Malamas +version: 5.2.0 +date-released: 2025-10-10 repository-code: https://github.com/boutproject/BOUT-dev url: http://boutproject.github.io/ -doi: 10.5281/zenodo.13753882 +doi: 10.5281/zenodo.17313945 license: 'LGPL-3.0-or-later' references: - type: article diff --git a/CMakeLists.txt b/CMakeLists.txt index c45fca3b72..194f97e845 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,55 +15,72 @@ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.22) cmake_policy(SET CMP0127 NEW) endif() -if ("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") - option(BOUT_ALLOW_INSOURCE_BUILD "Whether BOUT++ should really allow to build in source." OFF) - if (NOT ${BOUT_ALLOW_INSOURCE_BUILD}) - message(FATAL_ERROR "BOUT++ does not recommend in source builds. Try building out of source, e.g. with `cmake -S . -B build` or set -DBOUT_ALLOW_INSOURCE_BUILD=ON - but things may break!") +if("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + option(BOUT_ALLOW_INSOURCE_BUILD + "Whether BOUT++ should really allow to build in source." OFF + ) + if(NOT ${BOUT_ALLOW_INSOURCE_BUILD}) + message( + FATAL_ERROR + "BOUT++ does not recommend in source builds. Try building out of source, e.g. with `cmake -S . -B build` or set -DBOUT_ALLOW_INSOURCE_BUILD=ON - but things may break!" + ) endif() endif() # CMake currently doesn't support proper semver # Set the version here, strip any extra tags to use in `project` # We try to use git to get a full description, inspired by setuptools_scm -set(_bout_previous_version "5.1.1") -set(_bout_next_version "5.2.0") +set(_bout_previous_version "5.2.0") +set(_bout_next_version "5.2.1") execute_process( COMMAND "git" describe --tags --match=v${_bout_previous_version} - COMMAND sed -e s/${_bout_previous_version}-/${_bout_next_version}.dev/ -e s/-/+/ - RESULTS_VARIABLE error_codes + COMMAND sed -e s/${_bout_previous_version}-/${_bout_next_version}.dev/ -e + s/-/+/ RESULTS_VARIABLE error_codes OUTPUT_VARIABLE BOUT_FULL_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ) +) foreach(error_code ${error_codes}) - if (NOT ${error_code} STREQUAL 0) + if(NOT ${error_code} STREQUAL 0) set(BOUT_FULL_VERSION ${_bout_next_version}) endif() endforeach() # Remove leading "v" string(REGEX REPLACE "^v(.*)" "\\1" BOUT_FULL_VERSION ${BOUT_FULL_VERSION}) # Remove trailing tag -string(REGEX REPLACE "^([0-9]+\.[0-9]+\.[0-9]+)\..*" "\\1" BOUT_CMAKE_ACCEPTABLE_VERSION ${BOUT_FULL_VERSION}) +string(REGEX REPLACE "^([0-9]+\.[0-9]+\.[0-9]+)\..*" "\\1" + BOUT_CMAKE_ACCEPTABLE_VERSION ${BOUT_FULL_VERSION} +) # Get the trailing tag -string(REGEX REPLACE "^[0-9]+\.[0-9]+\.[0-9]+\.(.*)" "\\1" BOUT_VERSION_TAG ${BOUT_FULL_VERSION}) +string(REGEX REPLACE "^[0-9]+\.[0-9]+\.[0-9]+\.(.*)" "\\1" BOUT_VERSION_TAG + ${BOUT_FULL_VERSION} +) message(STATUS "Configuring BOUT++ version ${BOUT_FULL_VERSION}") -project(BOUT++ +project( + BOUT++ DESCRIPTION "Fluid PDE solver framework" VERSION ${BOUT_CMAKE_ACCEPTABLE_VERSION} - LANGUAGES CXX) + LANGUAGES CXX +) include(CMakeDependentOption) option(BUILD_SHARED_LIBS "Build shared libs" ON) # Override default -option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" OFF) +option( + INSTALL_GTEST + "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" + OFF +) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) include(BOUT++functions) -option(BOUT_UPDATE_GIT_SUBMODULE "Check submodules are up-to-date during build" ON) +option(BOUT_UPDATE_GIT_SUBMODULE "Check submodules are up-to-date during build" + ON +) # Adapted from https://cliutils.gitlab.io/modern-cmake/chapters/projects/submodule.html # Update submodules as needed function(bout_update_submodules) @@ -73,335 +90,380 @@ function(bout_update_submodules) find_package(Git QUIET) if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") message(STATUS "Submodule update") - execute_process(COMMAND ${GIT_EXECUTABLE} -c submodule.recurse=false submodule update --init --recursive + execute_process( + COMMAND ${GIT_EXECUTABLE} -c submodule.recurse=false submodule update + --init --recursive WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE GIT_SUBMOD_RESULT) + RESULT_VARIABLE GIT_SUBMOD_RESULT + ) if(NOT GIT_SUBMOD_RESULT EQUAL "0") - message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + message( + FATAL_ERROR + "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules" + ) endif() endif() endfunction() set(BOUT_SOURCES - ./include/bout/adios_object.hxx - ./include/bout/array.hxx - ./include/bout/assert.hxx - ./include/bout/boundary_factory.hxx - ./include/bout/boundary_op.hxx - ./include/bout/boundary_region.hxx - ./include/bout/boundary_standard.hxx - ./include/bout/bout.hxx - ./include/bout/bout_enum_class.hxx - ./include/bout/bout_types.hxx - ./include/bout/build_config.hxx - ./include/bout/boutcomm.hxx - ./include/bout/boutexception.hxx - ./include/bout/caliper_wrapper.hxx - ./include/bout/constants.hxx - ./include/bout/coordinates.hxx - ./include/bout/coordinates_accessor.hxx - ./include/bout/cyclic_reduction.hxx - ./include/bout/dcomplex.hxx - ./include/bout/deriv_store.hxx - ./include/bout/derivs.hxx - ./include/bout/difops.hxx - ./include/bout/expr.hxx - ./include/bout/fft.hxx - ./include/bout/field.hxx - ./include/bout/field2d.hxx - ./include/bout/field3d.hxx - ./include/bout/field_accessor.hxx - ./include/bout/field_data.hxx - ./include/bout/field_factory.hxx - ./include/bout/fieldgroup.hxx - ./include/bout/fieldperp.hxx - ./include/bout/fv_ops.hxx - ./include/bout/generic_factory.hxx - ./include/bout/globalfield.hxx - ./include/bout/globalindexer.hxx - ./include/bout/globals.hxx - ./include/bout/griddata.hxx - ./include/bout/gyro_average.hxx - ./include/bout/hypre_interface.hxx - ./include/bout/index_derivs.hxx - ./include/bout/index_derivs_interface.hxx - ./include/bout/initialprofiles.hxx - ./include/bout/interpolation.hxx - ./include/bout/interpolation_xz.hxx - ./include/bout/interpolation_z.hxx - ./include/bout/invert/laplacexy.hxx - ./include/bout/invert/laplacexz.hxx - ./include/bout/invert_laplace.hxx - ./include/bout/invert_parderiv.hxx - ./include/bout/invert_pardiv.hxx - ./include/bout/invertable_operator.hxx - ./include/bout/lapack_routines.hxx - ./include/bout/macro_for_each.hxx - ./include/bout/mask.hxx - ./include/bout/mesh.hxx - ./include/bout/monitor.hxx - ./include/bout/mpi_wrapper.hxx - ./include/bout/msg_stack.hxx - ./include/bout/multiostream.hxx - ./include/bout/openmpwrap.hxx - ./include/bout/operatorstencil.hxx - ./include/bout/options.hxx - ./include/bout/options_io.hxx - ./include/bout/optionsreader.hxx - ./include/bout/output.hxx - ./include/bout/output_bout_types.hxx - ./include/bout/parallel_boundary_op.hxx - ./include/bout/parallel_boundary_region.hxx - ./include/bout/paralleltransform.hxx - ./include/bout/petsc_interface.hxx - ./include/bout/petsclib.hxx - ./include/bout/physicsmodel.hxx - ./include/bout/rajalib.hxx - ./include/bout/region.hxx - ./include/bout/rkscheme.hxx - ./include/bout/rvec.hxx - ./include/bout/scorepwrapper.hxx - ./include/bout/single_index_ops.hxx - ./include/bout/slepclib.hxx - ./include/bout/smoothing.hxx - ./include/bout/snb.hxx - ./include/bout/solver.hxx - ./include/bout/solverfactory.hxx - ./include/bout/sourcex.hxx - ./include/bout/stencils.hxx - ./include/bout/sundials_backports.hxx - ./include/bout/surfaceiter.hxx - ./include/bout/sys/expressionparser.hxx - ./include/bout/sys/generator_context.hxx - ./include/bout/sys/gettext.hxx - ./include/bout/sys/range.hxx - ./include/bout/sys/timer.hxx - ./include/bout/sys/type_name.hxx - ./include/bout/sys/uncopyable.hxx - ./include/bout/sys/uuid.h - ./include/bout/sys/variant.hxx - ./include/bout/template_combinations.hxx - ./include/bout/traits.hxx - ./include/bout/unused.hxx - ./include/bout/utils.hxx - ./include/bout/vecops.hxx - ./include/bout/vector2d.hxx - ./include/bout/vector3d.hxx - ./include/bout/where.hxx - ./src/bout++.cxx - ./src/bout++-time.hxx - ./src/field/field.cxx - ./src/field/field2d.cxx - ./src/field/field3d.cxx - ./src/field/field_data.cxx - ./src/field/field_factory.cxx - ./src/field/fieldgenerators.cxx - ./src/field/fieldgenerators.hxx - ./src/field/fieldgroup.cxx - ./src/field/fieldperp.cxx - ./src/field/generated_fieldops.cxx - ./src/field/globalfield.cxx - ./src/field/initialprofiles.cxx - ./src/field/vecops.cxx - ./src/field/vector2d.cxx - ./src/field/vector3d.cxx - ./src/field/where.cxx - ./src/invert/fft_fftw.cxx - ./src/invert/lapack_routines.cxx - ./src/invert/laplace/impls/cyclic/cyclic_laplace.cxx - ./src/invert/laplace/impls/cyclic/cyclic_laplace.hxx - ./src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx - ./src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.hxx - ./src/invert/laplace/impls/multigrid/multigrid_alg.cxx - ./src/invert/laplace/impls/multigrid/multigrid_laplace.cxx - ./src/invert/laplace/impls/multigrid/multigrid_laplace.hxx - ./src/invert/laplace/impls/multigrid/multigrid_solver.cxx - ./src/invert/laplace/impls/naulin/naulin_laplace.cxx - ./src/invert/laplace/impls/naulin/naulin_laplace.hxx - ./src/invert/laplace/impls/pcr/pcr.cxx - ./src/invert/laplace/impls/pcr/pcr.hxx - ./src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx - ./src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx - ./src/invert/laplace/impls/petsc/petsc_laplace.cxx - ./src/invert/laplace/impls/petsc/petsc_laplace.hxx - ./src/invert/laplace/impls/petsc3damg/petsc3damg.cxx - ./src/invert/laplace/impls/petsc3damg/petsc3damg.hxx - ./src/invert/laplace/impls/serial_band/serial_band.cxx - ./src/invert/laplace/impls/serial_band/serial_band.hxx - ./src/invert/laplace/impls/serial_tri/serial_tri.cxx - ./src/invert/laplace/impls/serial_tri/serial_tri.hxx - ./src/invert/laplace/impls/spt/spt.cxx - ./src/invert/laplace/impls/spt/spt.hxx - ./src/invert/laplace/impls/hypre3d/hypre3d_laplace.cxx - ./src/invert/laplace/impls/hypre3d/hypre3d_laplace.hxx - ./src/invert/laplace/invert_laplace.cxx - ./src/invert/laplacexy/laplacexy.cxx - ./include/bout/invert/laplacexy2.hxx - ./src/invert/laplacexy2/laplacexy2.cxx - ./include/bout/invert/laplacexy2_hypre.hxx - ./src/invert/laplacexy2/laplacexy2_hypre.cxx - ./src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx - ./src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.hxx - ./src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx - ./src/invert/laplacexz/impls/petsc/laplacexz-petsc.hxx - ./src/invert/laplacexz/laplacexz.cxx - ./src/invert/parderiv/impls/cyclic/cyclic.cxx - ./src/invert/parderiv/impls/cyclic/cyclic.hxx - ./src/invert/parderiv/invert_parderiv.cxx - ./src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx - ./src/invert/pardiv/impls/cyclic/pardiv_cyclic.hxx - ./src/invert/pardiv/invert_pardiv.cxx - ./src/mesh/boundary_factory.cxx - ./src/mesh/boundary_region.cxx - ./src/mesh/boundary_standard.cxx - ./src/mesh/coordinates.cxx - ./src/mesh/coordinates_accessor.cxx - ./src/mesh/data/gridfromfile.cxx - ./src/mesh/data/gridfromoptions.cxx - ./src/mesh/difops.cxx - ./src/mesh/fv_ops.cxx - ./src/mesh/impls/bout/boutmesh.cxx - ./src/mesh/impls/bout/boutmesh.hxx - ./src/mesh/index_derivs.cxx - ./src/mesh/interpolation_xz.cxx - ./src/mesh/interpolation/bilinear_xz.cxx - ./src/mesh/interpolation/hermite_spline_xz.cxx - ./src/mesh/interpolation/hermite_spline_z.cxx - ./src/mesh/interpolation/interpolation_z.cxx - ./src/mesh/interpolation/lagrange_4pt_xz.cxx - ./src/mesh/interpolation/monotonic_hermite_spline_xz.cxx - ./src/mesh/invert3x3.hxx - ./src/mesh/mesh.cxx - ./src/mesh/parallel/fci.cxx - ./src/mesh/parallel/fci.hxx - ./src/mesh/parallel/identity.cxx - ./src/mesh/parallel/shiftedmetric.cxx - ./src/mesh/parallel/shiftedmetricinterp.cxx - ./src/mesh/parallel/shiftedmetricinterp.hxx - ./src/mesh/parallel_boundary_op.cxx - ./src/mesh/parallel_boundary_region.cxx - ./src/mesh/surfaceiter.cxx - ./src/physics/gyro_average.cxx - ./src/physics/physicsmodel.cxx - ./src/physics/smoothing.cxx - ./src/physics/snb.cxx - ./src/physics/sourcex.cxx - ./src/solver/impls/adams_bashforth/adams_bashforth.cxx - ./src/solver/impls/adams_bashforth/adams_bashforth.hxx - ./src/solver/impls/arkode/arkode.cxx - ./src/solver/impls/arkode/arkode.hxx - ./src/solver/impls/cvode/cvode.cxx - ./src/solver/impls/cvode/cvode.hxx - ./src/solver/impls/euler/euler.cxx - ./src/solver/impls/euler/euler.hxx - ./src/solver/impls/ida/ida.cxx - ./src/solver/impls/ida/ida.hxx - ./src/solver/impls/imex-bdf2/imex-bdf2.cxx - ./src/solver/impls/imex-bdf2/imex-bdf2.hxx - ./src/solver/impls/petsc/petsc.cxx - ./src/solver/impls/petsc/petsc.hxx - ./src/solver/impls/power/power.cxx - ./src/solver/impls/power/power.hxx - ./src/solver/impls/pvode/pvode.cxx - ./src/solver/impls/pvode/pvode.hxx - ./src/solver/impls/rk3-ssp/rk3-ssp.cxx - ./src/solver/impls/rk3-ssp/rk3-ssp.hxx - ./src/solver/impls/rk4/rk4.cxx - ./src/solver/impls/rk4/rk4.hxx - ./src/solver/impls/rkgeneric/impls/cashkarp/cashkarp.cxx - ./src/solver/impls/rkgeneric/impls/cashkarp/cashkarp.hxx - ./src/solver/impls/rkgeneric/impls/rk4simple/rk4simple.cxx - ./src/solver/impls/rkgeneric/impls/rk4simple/rk4simple.hxx - ./src/solver/impls/rkgeneric/impls/rkf34/rkf34.cxx - ./src/solver/impls/rkgeneric/impls/rkf34/rkf34.hxx - ./src/solver/impls/rkgeneric/impls/rkf45/rkf45.cxx - ./src/solver/impls/rkgeneric/impls/rkf45/rkf45.hxx - ./src/solver/impls/rkgeneric/rkgeneric.cxx - ./src/solver/impls/rkgeneric/rkgeneric.hxx - ./src/solver/impls/rkgeneric/rkscheme.cxx - ./src/solver/impls/slepc/slepc.cxx - ./src/solver/impls/slepc/slepc.hxx - ./src/solver/impls/snes/snes.cxx - ./src/solver/impls/snes/snes.hxx - ./src/solver/impls/split-rk/split-rk.cxx - ./src/solver/impls/split-rk/split-rk.hxx - ./src/solver/solver.cxx - ./src/sys/adios_object.cxx - ./src/sys/bout_types.cxx - ./src/sys/boutcomm.cxx - ./src/sys/boutexception.cxx - ./src/sys/derivs.cxx - ./src/sys/expressionparser.cxx - ./src/sys/generator_context.cxx - ./include/bout/hyprelib.hxx - ./src/sys/hyprelib.cxx - ./src/sys/msg_stack.cxx - ./src/sys/options.cxx - ./src/sys/options/optionparser.hxx - ./src/sys/options/options_ini.cxx - ./src/sys/options/options_ini.hxx - ./src/sys/options/options_io.cxx - ./src/sys/options/options_netcdf.cxx - ./src/sys/options/options_netcdf.hxx - ./src/sys/options/options_adios.cxx - ./src/sys/options/options_adios.hxx - ./src/sys/optionsreader.cxx - ./src/sys/output.cxx - ./src/sys/petsclib.cxx - ./src/sys/range.cxx - ./src/sys/slepclib.cxx - ./src/sys/timer.cxx - ./src/sys/type_name.cxx - ./src/sys/utils.cxx - ${CMAKE_CURRENT_BINARY_DIR}/include/bout/revision.hxx - ${CMAKE_CURRENT_BINARY_DIR}/include/bout/version.hxx - ) - + ./include/bout/adios_object.hxx + ./include/bout/array.hxx + ./include/bout/assert.hxx + ./include/bout/boundary_factory.hxx + ./include/bout/boundary_op.hxx + ./include/bout/boundary_region.hxx + ./include/bout/boundary_standard.hxx + ./include/bout/bout.hxx + ./include/bout/bout_enum_class.hxx + ./include/bout/bout_types.hxx + ./include/bout/build_config.hxx + ./include/bout/boutcomm.hxx + ./include/bout/boutexception.hxx + ./include/bout/caliper_wrapper.hxx + ./include/bout/constants.hxx + ./include/bout/coordinates.hxx + ./include/bout/coordinates_accessor.hxx + ./include/bout/cyclic_reduction.hxx + ./include/bout/dcomplex.hxx + ./include/bout/deriv_store.hxx + ./include/bout/derivs.hxx + ./include/bout/difops.hxx + ./include/bout/expr.hxx + ./include/bout/fft.hxx + ./include/bout/field.hxx + ./include/bout/field2d.hxx + ./include/bout/field3d.hxx + ./include/bout/field_accessor.hxx + ./include/bout/field_data.hxx + ./include/bout/field_factory.hxx + ./include/bout/fieldops.hxx + ./include/bout/fieldgroup.hxx + ./include/bout/fieldperp.hxx + ./include/bout/fv_ops.hxx + ./include/bout/generic_factory.hxx + ./include/bout/globalfield.hxx + ./include/bout/globalindexer.hxx + ./include/bout/globals.hxx + ./include/bout/griddata.hxx + ./include/bout/gyro_average.hxx + ./include/bout/hypre_interface.hxx + ./include/bout/index_derivs.hxx + ./include/bout/index_derivs_interface.hxx + ./include/bout/initialprofiles.hxx + ./include/bout/interpolation.hxx + ./include/bout/interpolation_xz.hxx + ./include/bout/interpolation_z.hxx + ./include/bout/invert/laplacexy.hxx + ./include/bout/invert/laplacexz.hxx + ./include/bout/invert_laplace.hxx + ./include/bout/invert_parderiv.hxx + ./include/bout/invert_pardiv.hxx + ./include/bout/invertable_operator.hxx + ./include/bout/lapack_routines.hxx + ./include/bout/macro_for_each.hxx + ./include/bout/mask.hxx + ./include/bout/mesh.hxx + ./include/bout/monitor.hxx + ./include/bout/mpi_wrapper.hxx + ./include/bout/msg_stack.hxx + ./include/bout/multiostream.hxx + ./include/bout/openmpwrap.hxx + ./include/bout/operatorstencil.hxx + ./include/bout/options.hxx + ./include/bout/options_io.hxx + ./include/bout/optionsreader.hxx + ./include/bout/output.hxx + ./include/bout/output_bout_types.hxx + ./include/bout/parallel_boundary_op.hxx + ./include/bout/parallel_boundary_region.hxx + ./include/bout/paralleltransform.hxx + ./include/bout/petsc_interface.hxx + ./include/bout/petsclib.hxx + ./include/bout/physicsmodel.hxx + ./include/bout/rajalib.hxx + ./include/bout/region.hxx + ./include/bout/rkscheme.hxx + ./include/bout/rvec.hxx + ./include/bout/scorepwrapper.hxx + ./include/bout/single_index_ops.hxx + ./include/bout/slepclib.hxx + ./include/bout/smoothing.hxx + ./include/bout/snb.hxx + ./include/bout/solver.hxx + ./include/bout/solverfactory.hxx + ./include/bout/sourcex.hxx + ./include/bout/stencils.hxx + ./include/bout/sundials_backports.hxx + ./include/bout/surfaceiter.hxx + ./include/bout/sys/expressionparser.hxx + ./include/bout/sys/generator_context.hxx + ./include/bout/sys/gettext.hxx + ./include/bout/sys/range.hxx + ./include/bout/sys/timer.hxx + ./include/bout/sys/type_name.hxx + ./include/bout/sys/uncopyable.hxx + ./include/bout/sys/uuid.h + ./include/bout/sys/variant.hxx + ./include/bout/template_combinations.hxx + ./include/bout/tokamak_coordinates.hxx + ./include/bout/traits.hxx + ./include/bout/twiddle.hxx + ./include/bout/unused.hxx + ./include/bout/utils.hxx + ./include/bout/vecops.hxx + ./include/bout/vector2d.hxx + ./include/bout/vector3d.hxx + ./include/bout/where.hxx + ./src/bout++.cxx + ./src/bout++-time.hxx + ./src/field/field.cxx + ./src/field/field2d.cxx + ./src/field/field3d.cxx + ./src/field/field_data.cxx + ./src/field/field_factory.cxx + ./src/field/fieldgenerators.cxx + ./src/field/fieldgenerators.hxx + ./src/field/fieldgroup.cxx + ./src/field/fieldperp.cxx + ./src/field/generated_fieldops.cxx + ./src/field/globalfield.cxx + ./src/field/initialprofiles.cxx + ./src/field/vecops.cxx + ./src/field/vector2d.cxx + ./src/field/vector3d.cxx + ./src/field/where.cxx + ./src/invert/fft_fftw.cxx + ./src/invert/lapack_routines.cxx + ./src/invert/laplace/common_transform.cxx + ./src/invert/laplace/common_transform.hxx + ./src/invert/laplace/impls/cyclic/cyclic_laplace.cxx + ./src/invert/laplace/impls/cyclic/cyclic_laplace.hxx + ./src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx + ./src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.hxx + ./src/invert/laplace/impls/multigrid/multigrid_alg.cxx + ./src/invert/laplace/impls/multigrid/multigrid_laplace.cxx + ./src/invert/laplace/impls/multigrid/multigrid_laplace.hxx + ./src/invert/laplace/impls/multigrid/multigrid_solver.cxx + ./src/invert/laplace/impls/naulin/naulin_laplace.cxx + ./src/invert/laplace/impls/naulin/naulin_laplace.hxx + ./src/invert/laplace/impls/pcr/pcr.cxx + ./src/invert/laplace/impls/pcr/pcr.hxx + ./src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx + ./src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx + ./src/invert/laplace/impls/petsc/petsc_laplace.cxx + ./src/invert/laplace/impls/petsc/petsc_laplace.hxx + ./src/invert/laplace/impls/petsc3damg/petsc3damg.cxx + ./src/invert/laplace/impls/petsc3damg/petsc3damg.hxx + ./src/invert/laplace/impls/serial_band/serial_band.cxx + ./src/invert/laplace/impls/serial_band/serial_band.hxx + ./src/invert/laplace/impls/serial_tri/serial_tri.cxx + ./src/invert/laplace/impls/serial_tri/serial_tri.hxx + ./src/invert/laplace/impls/spt/spt.cxx + ./src/invert/laplace/impls/spt/spt.hxx + ./src/invert/laplace/impls/hypre3d/hypre3d_laplace.cxx + ./src/invert/laplace/impls/hypre3d/hypre3d_laplace.hxx + ./src/invert/laplace/invert_laplace.cxx + ./src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx + ./src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx + ./src/invert/laplacexy/impls/petsc/laplacexy-petsc.cxx + ./src/invert/laplacexy/impls/petsc/laplacexy-petsc.hxx + ./src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.cxx + ./src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.hxx + ./src/invert/laplacexy/laplacexy.cxx + ./src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx + ./src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.hxx + ./src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx + ./src/invert/laplacexz/impls/petsc/laplacexz-petsc.hxx + ./src/invert/laplacexz/laplacexz.cxx + ./src/invert/parderiv/impls/cyclic/cyclic.cxx + ./src/invert/parderiv/impls/cyclic/cyclic.hxx + ./src/invert/parderiv/invert_parderiv.cxx + ./src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx + ./src/invert/pardiv/impls/cyclic/pardiv_cyclic.hxx + ./src/invert/pardiv/invert_pardiv.cxx + ./src/mesh/boundary_factory.cxx + ./src/mesh/boundary_region.cxx + ./src/mesh/boundary_standard.cxx + ./src/mesh/coordinates.cxx + ./src/mesh/coordinates_accessor.cxx + ./src/mesh/data/gridfromfile.cxx + ./src/mesh/data/gridfromoptions.cxx + ./src/mesh/difops.cxx + ./src/mesh/fv_ops.cxx + ./src/mesh/impls/bout/boutmesh.cxx + ./src/mesh/impls/bout/boutmesh.hxx + ./src/mesh/index_derivs.cxx + ./src/mesh/interpolation_xz.cxx + ./src/mesh/interpolation/bilinear_xz.cxx + ./src/mesh/interpolation/hermite_spline_xz.cxx + ./src/mesh/interpolation/hermite_spline_z.cxx + ./src/mesh/interpolation/interpolation_z.cxx + ./src/mesh/interpolation/lagrange_4pt_xz.cxx + ./src/mesh/invert3x3.hxx + ./src/mesh/mesh.cxx + ./src/mesh/parallel/fci.cxx + ./src/mesh/parallel/fci.hxx + ./src/mesh/parallel/fci_comm.cxx + ./src/mesh/parallel/fci_comm.hxx + ./src/mesh/parallel/identity.cxx + ./src/mesh/parallel/shiftedmetric.cxx + ./src/mesh/parallel/shiftedmetricinterp.cxx + ./src/mesh/parallel/shiftedmetricinterp.hxx + ./src/mesh/parallel_boundary_op.cxx + ./src/mesh/parallel_boundary_region.cxx + ./src/mesh/surfaceiter.cxx + ./src/mesh/tokamak_coordinates.cxx + ./src/physics/gyro_average.cxx + ./src/physics/physicsmodel.cxx + ./src/physics/smoothing.cxx + ./src/physics/snb.cxx + ./src/physics/sourcex.cxx + ./src/solver/impls/adams_bashforth/adams_bashforth.cxx + ./src/solver/impls/adams_bashforth/adams_bashforth.hxx + ./src/solver/impls/arkode/arkode.cxx + ./src/solver/impls/arkode/arkode.hxx + ./src/solver/impls/cvode/cvode.cxx + ./src/solver/impls/cvode/cvode.hxx + ./src/solver/impls/euler/euler.cxx + ./src/solver/impls/euler/euler.hxx + ./src/solver/impls/ida/ida.cxx + ./src/solver/impls/ida/ida.hxx + ./src/solver/impls/imex-bdf2/imex-bdf2.cxx + ./src/solver/impls/imex-bdf2/imex-bdf2.hxx + ./src/solver/impls/petsc/petsc.cxx + ./src/solver/impls/petsc/petsc.hxx + ./src/solver/impls/power/power.cxx + ./src/solver/impls/power/power.hxx + ./src/solver/impls/pvode/pvode.cxx + ./src/solver/impls/pvode/pvode.hxx + ./src/solver/impls/rk3-ssp/rk3-ssp.cxx + ./src/solver/impls/rk3-ssp/rk3-ssp.hxx + ./src/solver/impls/rk4/rk4.cxx + ./src/solver/impls/rk4/rk4.hxx + ./src/solver/impls/rkgeneric/impls/cashkarp/cashkarp.cxx + ./src/solver/impls/rkgeneric/impls/cashkarp/cashkarp.hxx + ./src/solver/impls/rkgeneric/impls/rk4simple/rk4simple.cxx + ./src/solver/impls/rkgeneric/impls/rk4simple/rk4simple.hxx + ./src/solver/impls/rkgeneric/impls/rkf34/rkf34.cxx + ./src/solver/impls/rkgeneric/impls/rkf34/rkf34.hxx + ./src/solver/impls/rkgeneric/impls/rkf45/rkf45.cxx + ./src/solver/impls/rkgeneric/impls/rkf45/rkf45.hxx + ./src/solver/impls/rkgeneric/rkgeneric.cxx + ./src/solver/impls/rkgeneric/rkgeneric.hxx + ./src/solver/impls/rkgeneric/rkscheme.cxx + ./src/solver/impls/slepc/slepc.cxx + ./src/solver/impls/slepc/slepc.hxx + ./src/solver/impls/snes/snes.cxx + ./src/solver/impls/snes/snes.hxx + ./src/solver/impls/split-rk/split-rk.cxx + ./src/solver/impls/split-rk/split-rk.hxx + ./src/solver/petsc_preconditioner.cxx + ./src/solver/solver.cxx + ./src/sys/adios_object.cxx + ./src/sys/bout_types.cxx + ./src/sys/boutcomm.cxx + ./src/sys/boutexception.cxx + ./src/sys/derivs.cxx + ./src/sys/expressionparser.cxx + ./src/sys/generator_context.cxx + ./include/bout/hyprelib.hxx + ./src/sys/hyprelib.cxx + ./src/sys/msg_stack.cxx + ./src/sys/options.cxx + ./src/sys/options/optionparser.hxx + ./src/sys/options/options_ini.cxx + ./src/sys/options/options_ini.hxx + ./src/sys/options/options_io.cxx + ./src/sys/options/options_netcdf.cxx + ./src/sys/options/options_netcdf.hxx + ./src/sys/options/options_adios.cxx + ./src/sys/options/options_adios.hxx + ./src/sys/optionsreader.cxx + ./src/sys/output.cxx + ./src/sys/output_bout_types.cxx + ./src/sys/petsclib.cxx + ./src/sys/range.cxx + ./src/sys/slepclib.cxx + ./src/sys/timer.cxx + ./src/sys/type_name.cxx + ./src/sys/utils.cxx + ${CMAKE_CURRENT_BINARY_DIR}/include/bout/revision.hxx + ${CMAKE_CURRENT_BINARY_DIR}/include/bout/version.hxx +) find_package(Python3) find_package(ClangFormat) -if (Python3_FOUND AND ClangFormat_FOUND) +if(Python3_FOUND AND ClangFormat_FOUND) set(BOUT_GENERATE_FIELDOPS_DEFAULT ON) else() set(BOUT_GENERATE_FIELDOPS_DEFAULT OFF) endif() -execute_process(COMMAND ${Python3_EXECUTABLE} -c "import importlib.util ; import sys; sys.exit(importlib.util.find_spec(\"zoidberg\") is None)" - RESULT_VARIABLE zoidberg_FOUND) -if (zoidberg_FOUND EQUAL 0) +execute_process( + COMMAND + ${Python3_EXECUTABLE} -c + "import importlib.util ; import sys; sys.exit(importlib.util.find_spec(\"zoidberg\") is None)" + RESULT_VARIABLE zoidberg_FOUND +) +if(zoidberg_FOUND EQUAL 0) set(zoidberg_FOUND ON) else() set(zoidberg_FOUND OFF) endif() +message(STATUS "Found Zoidberg for FCI tests: ${zoidberg_FOUND}") -option(BOUT_GENERATE_FIELDOPS "Automatically re-generate the Field arithmetic operators from the Python templates. \ +option( + BOUT_GENERATE_FIELDOPS + "Automatically re-generate the Field arithmetic operators from the Python templates. \ Requires Python3, clang-format, and Jinja2. Turn this OFF to skip generating them if, for example, \ -you are unable to install the Jinja2 Python module. This is only important for BOUT++ developers." ${BOUT_GENERATE_FIELDOPS_DEFAULT}) +you are unable to install the Jinja2 Python module. This is only important for BOUT++ developers." + ${BOUT_GENERATE_FIELDOPS_DEFAULT} +) -if (BOUT_GENERATE_FIELDOPS) - if (NOT Python3_FOUND) - message(FATAL_ERROR "python not found, but you have requested to generate code!") +if(BOUT_GENERATE_FIELDOPS) + if(NOT Python3_FOUND) + message( + FATAL_ERROR "python not found, but you have requested to generate code!" + ) endif() - if (NOT ClangFormat_FOUND) - message(FATAL_ERROR "clang-format not found, but you have requested to generate code!") + if(NOT ClangFormat_FOUND) + message( + FATAL_ERROR + "clang-format not found, but you have requested to generate code!" + ) + endif() + if(BOUT_ENABLE_RAJA) + set(GEN_LOOP_EXEC "raja") + elseif(BOUT_ENABLE_OPENMP) + set(GEN_LOOP_EXEC "openmp") + else() + set(GEN_LOOP_EXEC "serial") endif() - add_custom_command( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/field/generated_fieldops.cxx - COMMAND ${Python3_EXECUTABLE} gen_fieldops.py --filename generated_fieldops.cxx.tmp + add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/field/generated_fieldops.cxx + COMMAND ${Python3_EXECUTABLE} gen_fieldops.py --loop-exec ${GEN_LOOP_EXEC} + --filename generated_fieldops.cxx.tmp COMMAND ${ClangFormat_BIN} generated_fieldops.cxx.tmp -i - COMMAND ${CMAKE_COMMAND} -E rename generated_fieldops.cxx.tmp generated_fieldops.cxx - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/field/gen_fieldops.jinja ${CMAKE_CURRENT_SOURCE_DIR}/src/field/gen_fieldops.py + COMMAND ${CMAKE_COMMAND} -E rename generated_fieldops.cxx.tmp + generated_fieldops.cxx + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/field/gen_fieldops.jinja + ${CMAKE_CURRENT_SOURCE_DIR}/src/field/gen_fieldops.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/field/ - COMMENT "Generating source code" ) + COMMENT "Generating source code" + ) else() - message(AUTHOR_WARNING "'src/field/generated_fieldops.cxx' will not be \ + message( + AUTHOR_WARNING + "'src/field/generated_fieldops.cxx' will not be \ regenerated when you make changes to either \ 'src/field/gen_fieldops.py' or 'src/field/gen_fieldops.jinja'. \ This is because either Python3 or clang-format is missing \ (see above messages for more information) \ or BOUT_GENERATE_FIELDOPS is OFF (current value: ${BOUT_GENERATE_FIELDOPS}). \ This warning is only important for BOUT++ developers and can otherwise be \ -safely ignored.") +safely ignored." + ) endif() include(GNUInstallDirs) @@ -418,21 +480,29 @@ set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") # which point to directories outside the build tree to the install RPATH set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -execute_process(COMMAND ${Python3_EXECUTABLE} -c "import site ; print('/'.join(site.getusersitepackages().split('/')[-2:]))" +execute_process( + COMMAND + ${Python3_EXECUTABLE} -c + "import site ; print('/'.join(site.getusersitepackages().split('/')[-2:]))" RESULT_VARIABLE PYTHON_WORKING OUTPUT_VARIABLE PYTHON_SITEPATH_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE ) -set(CMAKE_INSTALL_PYTHON_SITEARCH lib/${PYTHON_SITEPATH_SUFFIX} CACHE STRING "Location to install python arch-specific modules") +set(CMAKE_INSTALL_PYTHON_SITEARCH + lib/${PYTHON_SITEPATH_SUFFIX} + CACHE STRING "Location to install python arch-specific modules" +) set(ON_OFF_AUTO ON OFF AUTO) -set(BOUT_ENABLE_PYTHON AUTO CACHE STRING "Build the Python interface") +set(BOUT_ENABLE_PYTHON + AUTO + CACHE STRING "Build the Python interface" +) set_property(CACHE BOUT_ENABLE_PYTHON PROPERTY STRINGS ${ON_OFF_AUTO}) -if (NOT BOUT_ENABLE_PYTHON IN_LIST ON_OFF_AUTO) +if(NOT BOUT_ENABLE_PYTHON IN_LIST ON_OFF_AUTO) message(FATAL_ERROR "BOUT_ENABLE_PYTHON must be one of ${ON_OFF_AUTO}") endif() -if (BOUT_ENABLE_PYTHON OR BOUT_ENABLE_PYTHON STREQUAL "AUTO") +if(BOUT_ENABLE_PYTHON OR BOUT_ENABLE_PYTHON STREQUAL "AUTO") add_subdirectory(tools/pylib/_boutpp_build) else() set(BOUT_ENABLE_PYTHON OFF) @@ -442,35 +512,37 @@ set(BOUT_USE_PYTHON ${BOUT_ENABLE_PYTHON}) # Ensure that the compile date/time is up-to-date when any of the sources change add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx - COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/cmake/GenerateDateTimeFile.cmake" + COMMAND ${CMAKE_COMMAND} -P + "${CMAKE_CURRENT_LIST_DIR}/cmake/GenerateDateTimeFile.cmake" DEPENDS ${BOUT_SOURCES} MAIN_DEPENDENCY "${CMAKE_CURRENT_LIST_DIR}/cmake/GenerateDateTimeFile.cmake" - ) - +) -add_library(bout++ - ${BOUT_SOURCES} - ${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx - ) +add_library(bout++ ${BOUT_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx) add_library(bout++::bout++ ALIAS bout++) target_link_libraries(bout++ PUBLIC MPI::MPI_CXX) -target_include_directories(bout++ PUBLIC - $ - $ - $ - ) +target_include_directories( + bout++ + PUBLIC $ + $ + $ +) set(BOUT_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/lib") -set_target_properties(bout++ PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${BOUT_LIB_PATH}" - ARCHIVE_OUTPUT_DIRECTORY "${BOUT_LIB_PATH}" - SOVERSION 5.1.0) +set_target_properties( + bout++ + PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${BOUT_LIB_PATH}" + ARCHIVE_OUTPUT_DIRECTORY "${BOUT_LIB_PATH}" + SOVERSION 5.2.0 +) # Set some variables for the bout-config script set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -L\$BOUT_LIB_PATH -lbout++") set(BOUT_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/include") -set(CONFIG_CFLAGS "${CONFIG_CFLAGS} -I\${BOUT_INCLUDE_PATH} -I${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CXX_FLAGS} -std=c++17") +set(CONFIG_CFLAGS + "${CONFIG_CFLAGS} -I\${BOUT_INCLUDE_PATH} -I${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CXX_FLAGS} -std=c++20" +) -target_compile_features(bout++ PUBLIC cxx_std_17) +target_compile_features(bout++ PUBLIC cxx_std_20) set_target_properties(bout++ PROPERTIES CXX_EXTENSIONS OFF) # Optional compiler features @@ -502,58 +574,77 @@ message(STATUS "Git revision: ${BOUT_REVISION}") # Build the file containing the version information configure_file( "${PROJECT_SOURCE_DIR}/include/bout/version.hxx.in" - "${PROJECT_BINARY_DIR}/include/bout/version.hxx") + "${PROJECT_BINARY_DIR}/include/bout/version.hxx" +) # Build the file containing just the commit hash # This will be rebuilt on every commit! configure_file( "${PROJECT_SOURCE_DIR}/include/bout/revision.hxx.in" - "${PROJECT_BINARY_DIR}/include/bout/revision.hxx") + "${PROJECT_BINARY_DIR}/include/bout/revision.hxx" +) ################################################## option(BOUT_ENABLE_WARNINGS "Enable compiler warnings" ON) -if (BOUT_ENABLE_WARNINGS) - target_compile_options(bout++ PRIVATE - $<$>: - $<$,$,$>: - -Wall -Wextra > > - $<$: - /W4 > - $<$:-Xcompiler=-Wall -Xcompiler=-Wextra > - ) - - include(EnableCXXWarningIfSupport) - # Note we explicitly turn off -Wcast-function-type as PETSc *requires* - # we cast a function to the wrong type in MatFDColoringSetFunction - target_enable_cxx_warning_if_supported(bout++ - FLAGS -Wnull-dereference -Wno-cast-function-type - ) +if(BOUT_ENABLE_WARNINGS) + target_compile_options( + bout++ + PRIVATE + $<$>: + $<$,$,$>: + -Wall + -Wextra + > + > + $<$: + /W4 + > + $<$:-Xcompiler=-Wall + -Xcompiler=-Wextra + > + ) + + include(EnableCXXWarningIfSupport) + # Note we explicitly turn off -Wcast-function-type as PETSc *requires* + # we cast a function to the wrong type in MatFDColoringSetFunction + target_enable_cxx_warning_if_supported( + bout++ FLAGS -Wnull-dereference -Wno-cast-function-type + ) endif() # Compile time features set(CHECK_LEVELS 0 1 2 3 4) -set(CHECK 2 CACHE STRING "Set run-time checking level") +set(CHECK + 2 + CACHE STRING "Set run-time checking level" +) set_property(CACHE CHECK PROPERTY STRINGS ${CHECK_LEVELS}) -if (NOT CHECK IN_LIST CHECK_LEVELS) +if(NOT CHECK IN_LIST CHECK_LEVELS) message(FATAL_ERROR "CHECK must be one of ${CHECK_LEVELS}") endif() message(STATUS "Runtime checking level: CHECK=${CHECK}") target_compile_definitions(bout++ PUBLIC "CHECK=${CHECK}") set(BOUT_CHECK_LEVEL ${CHECK}) -if (CHECK GREATER 1) +if(CHECK GREATER 1) set(bout_use_msgstack_default ON) else() set(bout_use_msgstack_default OFF) endif() -set(BOUT_ENABLE_MSGSTACK ${bout_use_msgstack_default} CACHE BOOL "Enable debug message stack") +set(BOUT_ENABLE_MSGSTACK + ${bout_use_msgstack_default} + CACHE BOOL "Enable debug message stack" +) message(STATUS "Message stack: BOUT_USE_MSGSTACK=${BOUT_ENABLE_MSGSTACK}") set(BOUT_USE_MSGSTACK ${BOUT_ENABLE_MSGSTACK}) -cmake_dependent_option(BOUT_ENABLE_OUTPUT_DEBUG "Enable extra debug output" OFF - "CHECK LESS 3" ON) -message(STATUS "Extra debug output: BOUT_USE_OUTPUT_DEBUG=${BOUT_ENABLE_OUTPUT_DEBUG}") +cmake_dependent_option( + BOUT_ENABLE_OUTPUT_DEBUG "Enable extra debug output" OFF "CHECK LESS 3" ON +) +message( + STATUS "Extra debug output: BOUT_USE_OUTPUT_DEBUG=${BOUT_ENABLE_OUTPUT_DEBUG}" +) set(BOUT_USE_OUTPUT_DEBUG ${BOUT_ENABLE_OUTPUT_DEBUG}) option(BOUT_ENABLE_SIGNAL "SegFault handling" ON) @@ -569,21 +660,12 @@ message(STATUS "Field name tracking: BOUT_USE_TRACK=${BOUT_ENABLE_TRACK}") set(BOUT_USE_TRACK ${BOUT_ENABLE_TRACK}) option(BOUT_ENABLE_SIGFPE "Signalling floating point exceptions" OFF) -message(STATUS "Signalling floating point exceptions: BOUT_USE_SIGFPE=${BOUT_ENABLE_SIGFPE}") +message( + STATUS + "Signalling floating point exceptions: BOUT_USE_SIGFPE=${BOUT_ENABLE_SIGFPE}" +) set(BOUT_USE_SIGFPE ${BOUT_ENABLE_SIGFPE}) -option(BOUT_ENABLE_BACKTRACE "Enable backtrace" ON) -if (BOUT_ENABLE_BACKTRACE) - find_program(ADDR2LINE_FOUND addr2line) - if (NOT ADDR2LINE_FOUND) - message(FATAL_ERROR "addr2line not found. Disable backtrace by setting BOUT_ENABLE_BACKTRACE=Off") - endif() - target_link_libraries(bout++ PUBLIC ${CMAKE_DL_LIBS}) - set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -l${CMAKE_DL_LIBS}") -endif() -message(STATUS "Enable backtrace: BOUT_USE_BACKTRACE=${BOUT_ENABLE_BACKTRACE}") -set(BOUT_USE_BACKTRACE ${BOUT_ENABLE_BACKTRACE}) - option(BOUT_ENABLE_METRIC_3D "Enable 3D metric support" OFF) if(BOUT_ENABLE_METRIC_3D) set(BOUT_METRIC_TYPE "3D") @@ -593,12 +675,15 @@ endif() set(BOUT_USE_METRIC_3D ${BOUT_ENABLE_METRIC_3D}) include(CheckCXXSourceCompiles) -check_cxx_source_compiles("int main() { const char* name = __PRETTY_FUNCTION__; }" - HAS_PRETTY_FUNCTION) +check_cxx_source_compiles( + "int main() { const char* name = __PRETTY_FUNCTION__; }" HAS_PRETTY_FUNCTION +) set(BOUT_HAS_PRETTY_FUNCTION ${HAS_PRETTY_FUNCTION}) # Locations of the various Python modules, including the generated boutconfig module -set(BOUT_PYTHONPATH "${CMAKE_CURRENT_BINARY_DIR}/tools/pylib:${CMAKE_CURRENT_SOURCE_DIR}/tools/pylib") +set(BOUT_PYTHONPATH + "${CMAKE_CURRENT_BINARY_DIR}/tools/pylib:${CMAKE_CURRENT_SOURCE_DIR}/tools/pylib" +) # Variables for boutconfig module -- note that these will contain # generator expressions and CMake targets, and not generally be very # useful @@ -611,18 +696,22 @@ get_target_property(BOUT_CFLAGS bout++ INTERFACE_INCLUDE_DIRECTORIES) # 1. Get the macro definitions. They come as a ;-separated list and # without the -D. We also need to also stick a -D on the front of # the first item -get_property(BOUT_COMPILE_DEFINITIONS +get_property( + BOUT_COMPILE_DEFINITIONS TARGET bout++ - PROPERTY COMPILE_DEFINITIONS) + PROPERTY COMPILE_DEFINITIONS +) string(REPLACE ";" " -D" BOUT_COMPILE_DEFINITIONS "${BOUT_COMPILE_DEFINITIONS}") string(CONCAT BOUT_COMPILE_DEFINITIONS " -D" "${BOUT_COMPILE_DEFINITIONS}") # 2. Get the compiler options. Again, they come as a ;-separated # list. Note that they don't include optimisation or debug flags: # they're in the CMAKE_CXX_FLAGS* variables -get_property(BOUT_COMPILE_OPTIONS +get_property( + BOUT_COMPILE_OPTIONS TARGET bout++ - PROPERTY COMPILE_OPTIONS) + PROPERTY COMPILE_OPTIONS +) string(REPLACE ";" " " BOUT_COMPILE_OPTIONS "${BOUT_COMPILE_OPTIONS}") # 3. The optimisation and/or debug flags are in the CMAKE_CXX_FLAGS* @@ -634,31 +723,33 @@ string(REPLACE ";" " " BOUT_COMPILE_OPTIONS "${BOUT_COMPILE_OPTIONS}") include(BuildType) # Here CMAKE_BUILD_TYPE is always set string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER) -string(CONCAT BOUT_COMPILE_BUILD_FLAGS - " " - "${CMAKE_CXX_FLAGS}" - "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}") +string(CONCAT BOUT_COMPILE_BUILD_FLAGS " " "${CMAKE_CXX_FLAGS}" + "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}" +) # 4. Now we join all the flags from the first three steps together -string(CONCAT BOUT_FLAGS_STRING - "${BOUT_COMPILE_OPTIONS}" - "${BOUT_COMPILE_DEFINITIONS}" - "${BOUT_COMPILE_BUILD_FLAGS}") +string(CONCAT BOUT_FLAGS_STRING "${BOUT_COMPILE_OPTIONS}" + "${BOUT_COMPILE_DEFINITIONS}" "${BOUT_COMPILE_BUILD_FLAGS}" +) # 5. Finally actually add the flags as a define -target_compile_definitions(bout++ - PRIVATE BOUT_FLAGS_STRING=${BOUT_FLAGS_STRING}) +target_compile_definitions( + bout++ PRIVATE BOUT_FLAGS_STRING=${BOUT_FLAGS_STRING} +) ################################################## # Tests # Are we building BOUT++ directly, or as part of another project -string(COMPARE EQUAL - "${PROJECT_NAME}" "${CMAKE_PROJECT_NAME}" - PROJECT_IS_TOP_LEVEL +string(COMPARE EQUAL "${PROJECT_NAME}" "${CMAKE_PROJECT_NAME}" + PROJECT_IS_TOP_LEVEL ) option(BOUT_TESTS "Build the tests" ${PROJECT_IS_TOP_LEVEL}) -option(BOUT_ENABLE_ALL_TESTS "Enable running all of the tests, rather then the standard selection of fast tests" OFF) +option( + BOUT_ENABLE_ALL_TESTS + "Enable running all of the tests, rather then the standard selection of fast tests" + OFF +) if(BOUT_TESTS) enable_testing() # Targets for just building the tests @@ -669,31 +760,35 @@ if(BOUT_TESTS) # Build all the tests add_custom_target(build-check) - add_dependencies(build-check build-check-unit-tests build-check-integrated-tests build-check-mms-tests) + add_dependencies( + build-check build-check-unit-tests build-check-integrated-tests + build-check-mms-tests + ) add_subdirectory(tests/unit EXCLUDE_FROM_ALL) add_subdirectory(tests/integrated EXCLUDE_FROM_ALL) add_subdirectory(tests/MMS EXCLUDE_FROM_ALL) # Targets for running the tests - if (BOUT_ENABLE_UNIT_TESTS) - add_custom_target(check-unit-tests - COMMAND ctest -R serial_tests --output-on-failure) + if(BOUT_ENABLE_UNIT_TESTS) + add_custom_target( + check-unit-tests COMMAND ctest -R serial_tests --output-on-failure + ) add_dependencies(check-unit-tests build-check-unit-tests) endif() - add_custom_target(check-integrated-tests - COMMAND ctest -R "test-" --output-on-failure) + add_custom_target( + check-integrated-tests COMMAND ctest -R "test-" --output-on-failure + ) add_dependencies(check-integrated-tests build-check-integrated-tests) - add_custom_target(check-mms-tests - COMMAND ctest -R "MMS-" --output-on-failure) + add_custom_target(check-mms-tests COMMAND ctest -R "MMS-" --output-on-failure) add_dependencies(check-mms-tests build-check-mms-tests) # Run all the tests add_custom_target(check) add_dependencies(check check-integrated-tests check-mms-tests) - if (BOUT_ENABLE_UNIT_TESTS) + if(BOUT_ENABLE_UNIT_TESTS) add_dependencies(check check-unit-tests) endif() @@ -705,21 +800,22 @@ if(BOUT_BUILD_EXAMPLES) add_subdirectory(examples EXCLUDE_FROM_ALL) endif() - ################################################## # L10N: localisation - include translations find_package(Gettext) -if (GETTEXT_FOUND) +if(GETTEXT_FOUND) #add_custom_target(mofiles ALL) set(bout_langs es de fr zh_CN zh_TW) foreach(_lang IN LISTS bout_langs) set(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/locale/${_lang}/libbout.gmo) set(_poFile ${CMAKE_CURRENT_SOURCE_DIR}/locale/${_lang}/libbout.po) - add_custom_command(OUTPUT ${_gmoFile} _mo_file_${_lang} - COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/locale/${_lang}/ + add_custom_command( + OUTPUT ${_gmoFile} _mo_file_${_lang} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_CURRENT_BINARY_DIR}/locale/${_lang}/ COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_poFile} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" DEPENDS ${_poFile} @@ -727,41 +823,46 @@ if (GETTEXT_FOUND) list(APPEND _gmoFiles ${_gmoFile}) - install(FILES ${_gmoFile} DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${_lang}/LC_MESSAGES/ RENAME libbout.mo) + install( + FILES ${_gmoFile} + DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${_lang}/LC_MESSAGES/ + RENAME libbout.mo + ) endforeach() - add_custom_target(mofiles ALL - DEPENDS ${_gmoFiles}) + add_custom_target(mofiles ALL DEPENDS ${_gmoFiles}) endif() - ################################################## # Documentation option(BOUT_BUILD_DOCS "Build the documentation" OFF) -if (BOUT_BUILD_DOCS) - add_subdirectory(manual EXCLUDE_FROM_ALL) +if(BOUT_BUILD_DOCS) + add_subdirectory(manual) endif() - -add_custom_target(dist - COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/pylib/_boutpp_build/backend.py dist +add_custom_target( + dist + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_SOURCE_DIR}/tools/pylib/_boutpp_build/backend.py dist # there is no cmake equivalent to `mv` - so only works on systems that are not inentionally non-POSIX complient COMMAND mv BOUT++-v${BOUT_FULL_VERSION}.tar.gz ${CMAKE_CURRENT_BINARY_DIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) +) ################################################## # Generate the build config header -if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include/bout/build_defines.hxx") +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include/bout/build_defines.hxx") # If we do in source builds, this is fine - if (NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) - message(FATAL_ERROR "Generated build_defines.hxx header already exists; please remove '${CMAKE_CURRENT_SOURCE_DIR}/include/bout/build_defines.hxx' before continuing") + if(NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) + message( + FATAL_ERROR + "Generated build_defines.hxx header already exists; please remove '${CMAKE_CURRENT_SOURCE_DIR}/include/bout/build_defines.hxx' before continuing" + ) endif() endif() configure_file(cmake_build_defines.hxx.in include/bout/build_defines.hxx) - ################################################## # Generate the bout-config script @@ -773,9 +874,11 @@ set(BOUT_HAS_PNETCDF OFF) # For shared libraries we only need to know how to link against BOUT++, # while for static builds we need the dependencies too -if (BUILD_SHARED_LIBS) +if(BUILD_SHARED_LIBS) # Include rpath linker flag so user doesn't need to set LD_LIBRARY_PATH - set(CONFIG_LDFLAGS "${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG}\$BOUT_LIB_PATH -L\$BOUT_LIB_PATH -lbout++ -lfmt ${CONFIG_LDFLAGS_SHARED}") + set(CONFIG_LDFLAGS + "${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG}\$BOUT_LIB_PATH -L\$BOUT_LIB_PATH -lbout++ -lfmt ${CONFIG_LDFLAGS_SHARED}" + ) else() set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS}") endif() @@ -784,9 +887,9 @@ set(ISINSTALLED "FALSE") set(_CONFIG_LDFLAGS) string(REPLACE " " ";" CONFIG_LDFLAGS_LIST ${CONFIG_LDFLAGS}) -foreach (flag ${CONFIG_LDFLAGS_LIST}) +foreach(flag ${CONFIG_LDFLAGS_LIST}) string(REGEX MATCH "^-.*$" isopt "${flag}") - if (isopt) + if(isopt) # message("${flag} is an option") set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} ${flag}") # All good @@ -796,70 +899,93 @@ foreach (flag ${CONFIG_LDFLAGS_LIST}) set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} ${flag}") else() string(FIND "${flag}" "::" hascolcol) - if (${hascolcol} EQUAL -1) - message("Fixing ${flag} to -l${flag}") - set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} -l${flag}") + if(${hascolcol} EQUAL -1) + message("Fixing ${flag} to -l${flag}") + set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} -l${flag}") else() - string(REGEX MATCH "[^:]*$" flag2 "${flag}") - message("Fixing ${flag} to -l${flag2}") - set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} -l${flag2}") + string(REGEX MATCH "[^:]*$" flag2 "${flag}") + message("Fixing ${flag} to -l${flag2}") + set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} -l${flag2}") endif() endif() endif() endforeach() -set( CONFIG_LDFLAGS ${_CONFIG_LDFLAGS}) +set(CONFIG_LDFLAGS ${_CONFIG_LDFLAGS}) # This version of the file allows the build directory to be used directly configure_file(bin/bout-config.in bin/bout-config @ONLY) -configure_file(tools/pylib/boutconfig/__init__.py.cin tools/pylib/boutconfig/__init__.py @ONLY) +configure_file( + tools/pylib/boutconfig/__init__.py.cin tools/pylib/boutconfig/__init__.py + @ONLY +) configure_file(bout++Config.cmake.in bout++Config.cmake @ONLY) # We need to generate a separate version for installation, with the # correct install paths. So first we need to replace the build # directory library path with the installation path -string(REPLACE - "${CMAKE_BINARY_DIR}/lib" "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" - CONFIG_LDFLAGS "${CONFIG_LDFLAGS}") +string(REPLACE "${CMAKE_BINARY_DIR}/lib" + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" CONFIG_LDFLAGS + "${CONFIG_LDFLAGS}" +) set(BOUT_LIB_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") # Update mpark.variant and fmt include paths if we're building them -if (NOT BOUT_USE_SYSTEM_MPARK_VARIANT) - set(MPARK_VARIANT_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") +if(NOT BOUT_USE_SYSTEM_MPARK_VARIANT) + set(MPARK_VARIANT_INCLUDE_PATH + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}" + ) endif() -if (NOT BOUT_USE_SYSTEM_FMT) +if(NOT BOUT_USE_SYSTEM_FMT) set(FMT_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") endif() +if(NOT BOUT_USE_SYSTEM_CPPTRACE) + set(CPPTRACE_INCLUDE_PATH + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}" + ) +endif() set(BOUT_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") # We don't need the build include path any more -string(REPLACE "-I${CMAKE_CURRENT_BINARY_DIR}/include" "" CONFIG_CFLAGS "${CONFIG_CFLAGS}") +string(REPLACE "-I${CMAKE_CURRENT_BINARY_DIR}/include" "" CONFIG_CFLAGS + "${CONFIG_CFLAGS}" +) set(PYTHONCONFIGPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_PYTHON_SITEARCH}") set(ISINSTALLED "TRUE") # This version now has the correct paths to use the final installation configure_file(bin/bout-config.in bin/bout-config-install @ONLY) -configure_file(tools/pylib/boutconfig/__init__.py.cin tools/pylib/boutconfig/__init__.py-install @ONLY) +configure_file( + tools/pylib/boutconfig/__init__.py.cin + tools/pylib/boutconfig/__init__.py-install @ONLY +) configure_file(bout++Config.cmake.in bout++Config.cmake-install @ONLY) ################################################## # Installation -install(TARGETS bout++ +install( + TARGETS bout++ EXPORT bout++Targets LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - ) + INCLUDES + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) # Repo files -install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING PATTERN "*.hxx") +install( + DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.hxx" +) # Generated headers install(DIRECTORY "${PROJECT_BINARY_DIR}/include/" - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) # The various helper scripts -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/" - USE_SOURCE_PERMISSIONS +install( + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/" USE_SOURCE_PERMISSIONS DESTINATION "${CMAKE_INSTALL_BINDIR}" REGEX "bout-squashoutput" EXCLUDE REGEX "bout-config\.in" EXCLUDE @@ -870,69 +996,71 @@ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/" # it. Note this MUST be done after the installation of bin/, to make # sure we clobber any versions of bout-config hanging around from an # autotools build -install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/bin/bout-config-install" +install( + PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/bin/bout-config-install" DESTINATION "${CMAKE_INSTALL_BINDIR}" RENAME "bout-config" - ) +) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/tools/pylib/boutconfig/__init__.py-install" DESTINATION "${CMAKE_INSTALL_PYTHON_SITEARCH}/boutconfig" RENAME "__init__.py" - ) +) include(CMakePackageConfigHelpers) write_basic_package_version_file( bout++ConfigVersion.cmake VERSION ${PACKAGE_VERSION} COMPATIBILITY SameMajorVersion - ) +) -install(EXPORT bout++Targets +install( + EXPORT bout++Targets FILE bout++Targets.cmake NAMESPACE bout++:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bout++" - ) +) # CMake configuration files install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/bout++ConfigVersion.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/BOUT++functions.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CorrectWindowsPaths.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindClangFormat.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindFFTW.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindHYPRE.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindnetCDF.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindnetCDFCxx.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPackageMultipass.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLibuuid.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPETSc.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindScoreP.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSLEPc.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSUNDIALS.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSphinx.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ResolveCompilerPaths.cmake" + FILES "${CMAKE_CURRENT_BINARY_DIR}/bout++ConfigVersion.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/BOUT++functions.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CorrectWindowsPaths.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindClangFormat.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindFFTW.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindHYPRE.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindnetCDF.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindnetCDFCxx.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPackageMultipass.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLibuuid.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPETSc.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindScoreP.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSLEPc.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSUNDIALS.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSphinx.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ResolveCompilerPaths.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bout++" - ) +) install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/bout++Config.cmake-install" + FILES "${CMAKE_CURRENT_BINARY_DIR}/bout++Config.cmake-install" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bout++" RENAME "bout++Config.cmake" - ) +) -export(EXPORT bout++Targets +export( + EXPORT bout++Targets FILE "${CMAKE_CURRENT_BINARY_DIR}/bout++Targets.cmake" NAMESPACE bout++:: - ) +) export(PACKAGE bout) ################################################## # Configure summary -message(" +message( + " -------------------------------- BOUT++ Configuration Summary -------------------------------- @@ -956,7 +1084,6 @@ message(" Output coloring : ${BOUT_USE_COLOR} Field name tracking : ${BOUT_USE_TRACK} Floating point exceptions: ${BOUT_USE_SIGFPE} - Backtrace enabled : ${BOUT_USE_BACKTRACE} RAJA enabled : ${BOUT_HAS_RAJA} Umpire enabled : ${BOUT_HAS_UMPIRE} Caliper enabled : ${BOUT_HAS_CALIPER} @@ -973,4 +1100,5 @@ message(" export PYTHONPATH=${BOUT_PYTHONPATH}:\$PYTHONPATH *** Now run `cmake --build ${CMAKE_BINARY_DIR}` to compile BOUT++ *** -") +" +) diff --git a/README.md b/README.md index c2e035e924..84062638fa 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Build Status](https://github.com/boutproject/BOUT-dev/actions/workflows/tests.yml/badge.svg?branch=next)](https://github.com/boutproject/BOUT-dev/actions) [![License](https://img.shields.io/badge/license-LGPL-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0.en.html) [![py3comp](https://img.shields.io/badge/py3-compatible-brightgreen.svg)](https://img.shields.io/badge/py3-compatible-brightgreen.svg) -[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.13753882.svg)](https://doi.org/10.5281/zenodo.8369888) +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.17313945.svg)](https://doi.org/10.5281/zenodo.17313945) ``` .______ ______ __ __ .___________. @@ -62,7 +62,7 @@ Homepage found at [http://boutproject.github.io/](http://boutproject.github.io/) BOUT++ needs the following: -* A C++17 compiler +* A C++20 compiler * MPI * NetCDF @@ -113,7 +113,7 @@ You can convert the CITATION.cff file into a Bibtex file as follows: See [CONTRIBUTING.md](CONTRIBUTING.md) and the [manual page](https://bout-dev.readthedocs.io/en/stable/developer_docs/contributing.html) ## License -Copyright 2010-2024 BOUT++ contributors +Copyright 2010-2026 BOUT++ contributors BOUT++ is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/bin/bout-add-mod-path b/bin/bout-add-mod-path index 9faf1be3de..ee7ea66d25 100755 --- a/bin/bout-add-mod-path +++ b/bin/bout-add-mod-path @@ -104,8 +104,7 @@ def create_mod(modulepath, name, top, build): else: prereq = "" with open(filename, "w") as f: - f.write( - f"""#%Module 1.0 + f.write(f"""#%Module 1.0 # # BOUT++ module for use with 'environment-modules' package # Created by bout-add-mod-path v0.9 @@ -119,17 +118,14 @@ setenv BOUT_TOP {top} prepend-path PATH {top}/bin prepend-path PYTHONPATH {top}/tools/pylib prepend-path IDL_PATH +{top}/tools/idllib:'' -""" - ) +""") if build != top: - f.write( - f"""#%Module 1.0 + f.write(f"""#%Module 1.0 setenv BOUT_BUILD {build} prepend-path PATH {build}/bin prepend-path LD_LIBRARY_PATH {build}/lib prepend-path PYTHONPATH {build}/tools/pylib -""" - ) +""") print(f"created `{filename}`") diff --git a/bin/bout-changelog-generator.py b/bin/bout-changelog-generator.py index cfb9a30bb0..d5ad11892e 100755 --- a/bin/bout-changelog-generator.py +++ b/bin/bout-changelog-generator.py @@ -1,4 +1,10 @@ #! /usr/bin/env python3 +# /// script +# requires-python = ">=3.12" +# dependencies = [ +# "pygithub", +# ] +# /// import argparse import os diff --git a/bin/bout-pylib-cmd-to-bin b/bin/bout-pylib-cmd-to-bin index 8f88a5dbf4..8d6dbed7ae 100755 --- a/bin/bout-pylib-cmd-to-bin +++ b/bin/bout-pylib-cmd-to-bin @@ -126,8 +126,7 @@ def createwrapper(mod, func_name, func, name): out += end f.write(out) - fprint( - """#!/usr/bin/env python3 + fprint("""#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK import argparse @@ -136,8 +135,7 @@ try: except ImportError: argcomplete=None -""" - ) +""") doc = True para = False ret = False @@ -183,19 +181,16 @@ except ImportError: arg_help[curarg].append(esc(blas)) # Print functions that are needed if "str_to_slice" in arg_type.values(): - fprint( - """ + fprint(""" def str_to_slice(sstr): args=[] for s in sstr.split(','): args.append(int(s)) print(args) return slice(*args) -""" - ) +""") if "str_to_bool" in arg_type.values(): - fprint( - """ + fprint(""" def str_to_bool(sstr): low=sstr.lower() # no or false @@ -206,8 +201,7 @@ def str_to_bool(sstr): return True else: raise ArgumentTypeError("Cannot parse %s to bool type"%sstr) -""" - ) +""") # Create the parser fprint("parser = argparse.ArgumentParser(%s)" % (esc(docs))) spec = inspect.signature(func) @@ -247,24 +241,19 @@ def str_to_bool(sstr): pre = "\n " fprint(")") - fprint( - """ + fprint(""" if argcomplete: argcomplete.autocomplete(parser) -# late import for faster auto-complete""" - ) +# late import for faster auto-complete""") fprint("from %s import %s" % (mod, func_name)) - fprint( - """ + fprint(""" args = parser.parse_args() # Call the function %s, using command line arguments %s(**args.__dict__) -""" - % (func_name, func_name) - ) +""" % (func_name, func_name)) # alternative, but I think 0o755 is easier to read # import stat # os.chmod(filename,stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH) diff --git a/bin/bout-v5-factory-upgrader.py b/bin/bout-v5-factory-upgrader.py index 29fc07db30..ee24572a94 100755 --- a/bin/bout-v5-factory-upgrader.py +++ b/bin/bout-v5-factory-upgrader.py @@ -5,7 +5,6 @@ import difflib import re - # Dictionary of factory methods that may need updating factories = { "Interpolation": { @@ -62,9 +61,7 @@ def find_factory_calls(factory, source): \s*=\s* {factory_name}:: .*{create_method}.* - """.format( - **factory - ), + """.format(**factory), source, re.VERBOSE, ) @@ -75,9 +72,7 @@ def find_type_pointers(factory, source): r""" \b{type_name}\s*\*\s* # Type name and pointer ([\w_]+)\s*; # Variable name - """.format( - **factory - ), + """.format(**factory), source, re.VERBOSE, ) @@ -107,9 +102,7 @@ def fix_declarations(factory, variables, source): (.*?)(class\s*)? # optional "class" keyword \b({type_name})\s*\*\s* # Type-pointer ({variable_name})\s*; # Variable - """.format( - type_name=factory["type_name"], variable_name=variable - ), + """.format(type_name=factory["type_name"], variable_name=variable), r"\1std::unique_ptr<\3> \4{nullptr};", source, flags=re.VERBOSE, @@ -123,9 +116,7 @@ def fix_declarations(factory, variables, source): ({variable_name})\s* # Variable =\s* # Assignment from factory ({factory_name}::.*{create_method}.*); - """.format( - variable_name=variable, **factory - ), + """.format(variable_name=variable, **factory), r"\1auto \4 = \5;", source, flags=re.VERBOSE, @@ -139,9 +130,7 @@ def fix_declarations(factory, variables, source): ({variable_name})\s* # Variable =\s* # Assignment (0|nullptr|NULL); - """.format( - variable_name=variable, **factory - ), + """.format(variable_name=variable, **factory), r"\1std::unique_ptr<\2> \3{nullptr};", source, flags=re.VERBOSE, diff --git a/bin/bout-v5-format-upgrader.py b/bin/bout-v5-format-upgrader.py index 7c7d13ac7f..a534ca240a 100755 --- a/bin/bout-v5-format-upgrader.py +++ b/bin/bout-v5-format-upgrader.py @@ -5,7 +5,6 @@ import difflib import re - format_replacements = { "c": "c", "d": "d", diff --git a/bin/bout-v5-header-upgrader.py b/bin/bout-v5-header-upgrader.py index 49a8fbcbe4..77794ab920 100755 --- a/bin/bout-v5-header-upgrader.py +++ b/bin/bout-v5-header-upgrader.py @@ -9,7 +9,6 @@ from typing import List from subprocess import run - header_shim_sentinel = "// BOUT++ header shim" header_warning = f"""\ @@ -122,8 +121,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Fix deprecated header locations for BOUT++ v4 -> v5 All BOUT++ headers are now under ``include/bout`` and @@ -142,8 +140,7 @@ def create_patch(filename, original, modified): If you have staged changes, this tool will not work, so to avoid committing undesired or unrelated changes. - """ - ), + """), ) parser.add_argument( diff --git a/bin/bout-v5-input-file-upgrader.py b/bin/bout-v5-input-file-upgrader.py index e2940ff58a..ea979005d5 100755 --- a/bin/bout-v5-input-file-upgrader.py +++ b/bin/bout-v5-input-file-upgrader.py @@ -271,8 +271,7 @@ def possibly_apply_patch(patch, options_file, quiet=False, force=False): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Fix input files for BOUT++ v5+ Please note that this will only fix input options in sections with @@ -300,8 +299,7 @@ def possibly_apply_patch(patch, options_file, quiet=False, force=False): Files that change in this way will have the "canonicalisation" patch presented first. If you choose not to apply this patch, the "upgrade - fixer" patch will still include it.""" - ), + fixer" patch will still include it."""), ) parser.add_argument("files", action="store", nargs="+", help="Input files") diff --git a/bin/bout-v5-macro-upgrader.py b/bin/bout-v5-macro-upgrader.py index 11b4926255..d644fed9e8 100755 --- a/bin/bout-v5-macro-upgrader.py +++ b/bin/bout-v5-macro-upgrader.py @@ -342,8 +342,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Fix macro defines for BOUT++ v4 -> v5 Please note that this is only slightly better than dumb text replacement. It @@ -359,8 +358,7 @@ def create_patch(filename, original, modified): still replace them in strings or comments. Please check the diff output carefully! - """ - ), + """), ) parser.add_argument("files", action="store", nargs="+", help="Input files") diff --git a/bin/bout-v5-physics-model-upgrader.py b/bin/bout-v5-physics-model-upgrader.py index 26fb8ef6e0..260d1d59ee 100755 --- a/bin/bout-v5-physics-model-upgrader.py +++ b/bin/bout-v5-physics-model-upgrader.py @@ -8,7 +8,6 @@ import textwrap import warnings - PHYSICS_MODEL_INCLUDE = '#include "bout/physicsmodel.hxx"' PHYSICS_MODEL_SKELETON = """ @@ -213,13 +212,11 @@ def fix_bout_constrain(source, error_on_warning): "\n ".join(["{}:{}".format(i, source_lines[i]) for i in line_range]) ) - message = textwrap.dedent( - """\ + message = textwrap.dedent("""\ Some uses of `bout_constrain` remain, but we could not automatically convert them to use `Solver::constraint`. Please fix them before continuing: - """ - ) + """) message += " " + "\n ".join(lines_context) if error_on_warning: @@ -389,8 +386,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Upgrade legacy physics models to use the PhysicsModel class This will do the bare minimum required to compile, and @@ -403,8 +399,7 @@ def create_patch(filename, original, modified): By default, this will use the file name stripped of file extensions as the name of the new class. Use '--name=' to give a different name. - """ - ), + """), ) parser.add_argument("files", action="store", nargs="+", help="Files to fix") diff --git a/bin/bout-v5-xzinterpolation-upgrader.py b/bin/bout-v5-xzinterpolation-upgrader.py index 37c79e0de8..e70c3c54ae 100755 --- a/bin/bout-v5-xzinterpolation-upgrader.py +++ b/bin/bout-v5-xzinterpolation-upgrader.py @@ -54,9 +54,7 @@ def fix_header_includes(old_header, new_header, source): (<|") ({header}) # Header name (>|") - """.format( - header=old_header - ), + """.format(header=old_header), r"\1\2{header}\4".format(header=new_header), source, flags=re.VERBOSE, @@ -67,9 +65,7 @@ def fix_interpolations(old_interpolation, new_interpolation, source): return re.sub( r""" \b{}\b - """.format( - old_interpolation - ), + """.format(old_interpolation), r"{}".format(new_interpolation), source, flags=re.VERBOSE, @@ -120,9 +116,7 @@ def fix_factories(old_factory, new_factory, source): return re.sub( r""" \b{}\b - """.format( - old_factory - ), + """.format(old_factory), r"{}".format(new_factory), source, flags=re.VERBOSE, diff --git a/bin/update_citations.py b/bin/update_citations.py index c7771a3611..63f597f588 100755 --- a/bin/update_citations.py +++ b/bin/update_citations.py @@ -1,4 +1,11 @@ #!/usr/bin/env python3 +# /// script +# requires-python = ">=3.11" +# dependencies = [ +# "ruamel-yaml", +# "unidecode", +# ] +# /// import argparse import subprocess from collections import defaultdict @@ -242,6 +249,9 @@ def update_citations(): new_authors = [] for author in unrecognised_authors: + if " " not in author: + # This is just a GitHub handle, skip + continue first_name, last_name = author.rsplit(maxsplit=1) new_authors.append({"family-names": last_name, "given-names": first_name}) diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index 85df930aea..3e9758a8c1 100755 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -66,11 +66,6 @@ def update_version_number_in_file(relative_filepath, pattern, new_version_number def bump_version_numbers( new_version_number: VersionNumber, next_version_number: VersionNumber ): - update_version_number_in_file( - "configure.ac", - r"^AC_INIT\(\[BOUT\+\+\],\[(\d+\.\d+\.\d+)\]", - new_version_number, - ) update_version_number_in_file( "CITATION.cff", r"^version: (\d+\.\d+\.\d+)", new_version_number ) @@ -163,8 +158,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Update the software version number to the specified version, to be given in the form major.minor.patch, e.g. 5.10.3 @@ -177,8 +171,7 @@ def create_patch(filename, original, modified): the 'minor' version number of the provided version will be incremented by 1, e.g. 5.10.3 -> 5.11.3 - """ - ), + """), ) parser.add_argument( diff --git a/bout++Config.cmake.in b/bout++Config.cmake.in index d461e58e4b..5cc68aefde 100644 --- a/bout++Config.cmake.in +++ b/bout++Config.cmake.in @@ -6,10 +6,9 @@ set(BOUT_USE_SIGNAL @BOUT_USE_SIGNAL@) set(BOUT_USE_COLOR @BOUT_USE_COLOR@) set(BOUT_USE_TRACK @BOUT_USE_TRACK@) set(BOUT_USE_SIGFPE @BOUT_USE_SIGFPE@) -set(BOUT_USE_BACKTRACE @BOUT_USE_BACKTRACE@) set(BOUT_USE_OPENMP @BOUT_USE_OPENMP@) set(BOUT_HAS_CUDA @BOUT_HAS_CUDA@) -set(BOUT_HAS_OUTPUT_DEBUG @BOUT_HAS_OUTPUT_DEBUG@) +set(BOUT_USE_OUTPUT_DEBUG @BOUT_USE_OUTPUT_DEBUG@) set(BOUT_CHECK_LEVEL @BOUT_CHECK_LEVEL@) set(BOUT_USE_METRIC_3D @BOUT_USE_METRIC_3D@) @@ -109,6 +108,14 @@ elseif(EXISTS "@ADIOS2_BINARY_DIR@") # If we downloaded ADIOS2, then we need to add its build directory to our search paths list(APPEND CMAKE_PREFIX_PATH "@ADIOS2_BINARY_DIR@") endif() +if(EXISTS "@cpptrace_ROOT@") + set(cpptrace_ROOT "@cpptrace_ROOT@") +elseif(EXISTS "@cpptrace_BINARY_DIR@") + list(APPEND CMAKE_PREFIX_PATH "@cpptrace_BINARY_DIR@") + list(APPEND CMAKE_PREFIX_PATH "@cpptrace_BINARY_DIR@/cmake") + list(APPEND CMAKE_PREFIX_PATH "@libdwarf_BINARY_DIR@/src/lib/libdwarf") + list(APPEND CMAKE_PREFIX_PATH "@zstd_BINARY_DIR@") +endif() if(@BOUT_USE_SYSTEM_MPARK_VARIANT@) set(mpark_variant_ROOT "@mpark_variant_ROOT@") @@ -160,6 +167,7 @@ if (BOUT_HAS_GETTEXT) endif() find_dependency(mpark_variant @mpark_variant_VERSION@) find_dependency(fmt @fmt_VERSION@) +find_dependency(cpptrace @cpptrace_VERSION@) if (BOUT_HAS_SLEPC) find_dependency(SLEPc @SLEPC_VERSION@) endif() @@ -174,3 +182,30 @@ if (BOUT_HAS_ADIOS2) endif() include("${CMAKE_CURRENT_LIST_DIR}/bout++Targets.cmake") + +add_library(bout++ INTERFACE IMPORTED) +set_target_properties(bout++ + PROPERTIES BOUT_USE_SIGNAL @BOUT_USE_SIGNAL@ + BOUT_USE_COLOR @BOUT_USE_COLOR@ + BOUT_USE_TRACK @BOUT_USE_TRACK@ + BOUT_USE_SIGFPE @BOUT_USE_SIGFPE@ + BOUT_USE_OPENMP @BOUT_USE_OPENMP@ + BOUT_HAS_CUDA @BOUT_HAS_CUDA@ + BOUT_USE_OUTPUT_DEBUG @BOUT_USE_OUTPUT_DEBUG@ + BOUT_CHECK_LEVEL @BOUT_CHECK_LEVEL@ + BOUT_USE_METRIC_3D @BOUT_USE_METRIC_3D@ + BOUT_HAS_PVODE @BOUT_HAS_PVODE@ + BOUT_HAS_NETCDF @BOUT_HAS_NETCDF@ + BOUT_HAS_ADIOS2 @BOUT_HAS_ADIOS2@ + BOUT_HAS_FFTW @BOUT_HAS_FFTW@ + BOUT_HAS_LAPACK @BOUT_HAS_LAPACK@ + BOUT_HAS_PETSC @BOUT_HAS_PETSC@ + BOUT_HAS_SLEPC @BOUT_HAS_SLEPC@ + BOUT_HAS_SCOREP @BOUT_HAS_SCOREP@ + BOUT_USE_UUID_SYSTEM_GENERATOR @BOUT_USE_UUID_SYSTEM_GENERATOR@ + BOUT_HAS_SUNDIALS @BOUT_HAS_SUNDIALS@ + BOUT_HAS_HYPRE @BOUT_HAS_HYPRE@ + BOUT_HAS_GETTEXT @BOUT_HAS_GETTEXT@ + BOUT_HAS_UMPIRE @BOUT_HAS_UMPIRE@ + BOUT_HAS_RAJA @BOUT_HAS_RAJA@ +) diff --git a/build-aux/config.guess b/build-aux/config.guess deleted file mode 100755 index c6fad2f5e5..0000000000 --- a/build-aux/config.guess +++ /dev/null @@ -1,1568 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright 1992-2013 Free Software Foundation, Inc. - -timestamp='2013-06-10' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). -# -# Originally written by Per Bothner. -# -# You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD -# -# Please send patches with a ChangeLog entry to config-patches@gnu.org. - - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright 1992-2013 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -trap 'exit 1' 1 2 15 - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -case "${UNAME_SYSTEM}" in -Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu - - eval $set_cc_for_build - cat <<-EOF > $dummy.c - #include - #if defined(__UCLIBC__) - LIBC=uclibc - #elif defined(__dietlibc__) - LIBC=dietlibc - #else - LIBC=gnu - #endif - EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` - ;; -esac - -case "${UNAME_MACHINE}" in - i?86) - test -z "$VENDOR" && VENDOR=pc - ;; - *) - test -z "$VENDOR" && VENDOR=unknown - ;; -esac -test -f /etc/SuSE-release -o -f /.buildenv && VENDOR=suse - -# Note: order is significant - the case branches are not exclusive. - -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - sh5el) machine=sh5le-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ELF__ - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in - Debian*) - release='-gnu' - ;; - *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit ;; - *:Bitrig:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo ${UNAME_MACHINE_ARCH}-${VENDOR}-bitrig${UNAME_RELEASE} - exit ;; - *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-${VENDOR}-openbsd${UNAME_RELEASE} - exit ;; - *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-ekkobsd${UNAME_RELEASE} - exit ;; - *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-solidbsd${UNAME_RELEASE} - exit ;; - macppc:MirBSD:*:*) - echo powerpc-${VENDOR}-mirbsd${UNAME_RELEASE} - exit ;; - *:MirBSD:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-mirbsd${UNAME_RELEASE} - exit ;; - alpha:OSF1:*:*) - case $UNAME_RELEASE in - *4.0) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - ;; - *5.*) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` - ;; - esac - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE="alpha" ;; - "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; - "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; - "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; - "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; - "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; - "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; - "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; - "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; - "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; - "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; - "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; - esac - # A Pn.n version is a patched version. - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-${VENDOR}-sysv4 - exit ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-amigaos - exit ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-morphos - exit ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; - *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; - *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; - arm*:riscos:*:*|arm*:RISCOS:*:*) - echo arm-${VENDOR}-riscos - exit ;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; - DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; - s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} - exit ;; - i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH="i386" - # If there is a compiler, see if it is configured for 64-bit objects. - # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. - # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - SUN_ARCH="x86_64" - fi - fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos${UNAME_RELEASE} - ;; - sun4) - echo sparc-sun-sunos${UNAME_RELEASE} - ;; - esac - exit ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-${VENDOR}-mint${UNAME_RELEASE} - exit ;; - m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; - powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && - { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] - then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] - then - echo m88k-dg-dgux${UNAME_RELEASE} - else - echo m88k-dg-dguxbcs${UNAME_RELEASE} - fi - else - echo i586-dg-dgux${UNAME_RELEASE} - fi - exit ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; - *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; - ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` - then - echo "$SYSTEM_NAME" - else - echo rs6000-ibm-aix3.2.5 - fi - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit ;; - *:AIX:*:[4567]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 - esac ;; - esac - fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if [ ${HP_ARCH} = "hppa2.0w" ] - then - eval $set_cc_for_build - - # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating - # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler - # generating 64-bit code. GNU and HP use different nomenclature: - # - # $ CC_FOR_BUILD=cc ./config.guess - # => hppa2.0w-hp-hpux11.23 - # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess - # => hppa64-hp-hpux11.23 - - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | - grep -q __LP64__ - then - HP_ARCH="hppa2.0w" - else - HP_ARCH="hppa64" - fi - fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; - ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; - 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; - i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-${VENDOR}-osf1mk - else - echo ${UNAME_MACHINE}-${VENDOR}-osf1 - fi - exit ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; - sparc*:BSD/OS:*:*) - echo sparc-${VENDOR}-bsdi${UNAME_RELEASE} - exit ;; - *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-bsdi${UNAME_RELEASE} - exit ;; - *:FreeBSD:*:*) - UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in - amd64) - echo x86_64-${VENDOR}-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-${VENDOR}-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - esac - exit ;; - i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; - *:MINGW64*:*) - echo ${UNAME_MACHINE}-pc-mingw64 - exit ;; - *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; - i*:MSYS*:*) - echo ${UNAME_MACHINE}-pc-msys - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; - i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; - *:Interix*:*) - case ${UNAME_MACHINE} in - x86) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; - authenticamd | genuineintel | EM64T) - echo x86_64-${VENDOR}-interix${UNAME_RELEASE} - exit ;; - IA64) - echo ia64-${VENDOR}-interix${UNAME_RELEASE} - exit ;; - esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; - i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; - amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-${VENDOR}-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-${VENDOR}-cygwin - exit ;; - prep*:SunOS:5.*:*) - echo powerpcle-${VENDOR}-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - *:GNU:*:*) - # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-${VENDOR}-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; - *:GNU/*:*:*) - # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-${VENDOR}-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; - aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - aarch64_be:Linux:*:*) - UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="gnulibc1" ; fi - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - arm*:Linux:*:*) - eval $set_cc_for_build - if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_EABI__ - then - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - else - if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_PCS_VFP - then - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC}eabi - else - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC}eabihf - fi - fi - exit ;; - avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; - crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; - frv:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - i*86:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} - exit ;; - ia64:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - m68*:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} - #else - CPU= - #endif - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-${VENDOR}-linux-${LIBC}"; exit; } - ;; - or1k:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - or32:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - padre:Linux:*:*) - echo sparc-${VENDOR}-linux-${LIBC} - exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-${VENDOR}-linux-${LIBC} - exit ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-${VENDOR}-linux-${LIBC} ;; - PA8*) echo hppa2.0-${VENDOR}-linux-${LIBC} ;; - *) echo hppa-${VENDOR}-linux-${LIBC} ;; - esac - exit ;; - ppc64:Linux:*:*) - echo powerpc64-${VENDOR}-linux-${LIBC} - exit ;; - ppc:Linux:*:*) - echo powerpc-${VENDOR}-linux-${LIBC} - exit ;; - ppc64le:Linux:*:*) - echo powerpc64le-${VENDOR}-linux-${LIBC} - exit ;; - ppcle:Linux:*:*) - echo powerpcle-${VENDOR}-linux-${LIBC} - exit ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux-${LIBC} - exit ;; - sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - sh*:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - tile*:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-${LIBC} - exit ;; - x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-linux-${LIBC} - exit ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; - i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-${VENDOR}-stop - exit ;; - i*86:atheos:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-atheos - exit ;; - i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-${VENODR}-lynxos${UNAME_RELEASE} - exit ;; - i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} - fi - exit ;; - i*86:*:5:[678]*) - # UnixWare 7.x, OpenUNIX and OpenServer 6. - case `/bin/uname -X | grep "^Machine"` in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo ${UNAME_MACHINE}-${VENDOR}-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL - else - echo ${UNAME_MACHINE}-pc-sysv32 - fi - exit ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i586. - # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that - # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-${VENODR}-sysv${UNAME_RELEASE} # Unknown i860-SVR4 - fi - exit ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; - M68*:*:R3V[5678]*:*) - test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; - 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4; exit; } ;; - NCR*:*:4.2:* | MPRAS*:*:4.2:*) - OS_REL='.3' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-${VENDOR}-lynxos${UNAME_RELEASE} - exit ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-${VENDOR}-lynxos${UNAME_RELEASE} - exit ;; - rs6000:LynxOS:2.*:*) - echo rs6000-${VENDOR}-lynxos${UNAME_RELEASE} - exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-${VENDOR}-lynxos${UNAME_RELEASE} - exit ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; - i*86:VOS:*:*) - # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} - else - echo mips-${VENDOR}-sysv${UNAME_RELEASE} - fi - exit ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; - BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; - x86_64:Haiku:*:*) - echo x86_64-${VENDOR}-haiku - exit ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; - SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} - exit ;; - SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} - exit ;; - SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} - exit ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval $set_cc_for_build - if test "$UNAME_PROCESSOR" = unknown ; then - UNAME_PROCESSOR=powerpc - fi - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi - fi - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-?:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} - exit ;; - NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; - DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - if test "$cputype" = "386"; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo ${UNAME_MACHINE}-${VENDOR}-plan9 - exit ;; - *:TOPS-10:*:*) - echo pdp10-${VENDOR}-tops10 - exit ;; - *:TENEX:*:*) - echo pdp10-${VENDOR}-tenex - exit ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; - *:TOPS-20:*:*) - echo pdp10-${VENDOR}-tops20 - exit ;; - *:ITS:*:*) - echo pdp10-${VENDOR}-its - exit ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; - *:DragonFly:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; - *:*VMS:*:*) - UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; - esac ;; - *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; - i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' - exit ;; - i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos - exit ;; - i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros - exit ;; - x86_64:VMkernel:*:*) - echo ${UNAME_MACHINE}-${VENDOR}-esx - exit ;; -esac - -eval $set_cc_for_build -cat >$dummy.c < -# include -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -# if !defined (ultrix) -# include -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - c34*) - echo c34-convex-bsd - exit ;; - c38*) - echo c38-convex-bsd - exit ;; - c4*) - echo c4-convex-bsd - exit ;; - esac -fi - -cat >&2 < in order to provide the needed -information to handle your system. - -config.guess timestamp = $timestamp - -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} -EOF - -exit 1 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/build-aux/config.rpath b/build-aux/config.rpath deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/build-aux/config.sub b/build-aux/config.sub deleted file mode 100755 index 8b612ab89d..0000000000 --- a/build-aux/config.sub +++ /dev/null @@ -1,1788 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright 1992-2013 Free Software Foundation, Inc. - -timestamp='2013-04-24' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). - - -# Please send patches with a ChangeLog entry to config-patches@gnu.org. -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS - -Canonicalize a configuration name. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright 1992-2013 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo $1 - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | \ - kopensolaris*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - android-linux) - os=-linux-android - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray | -microblaze*) - os= - basic_machine=$1 - ;; - -bluegene*) - os=-cnk - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco6) - os=-sco5v6 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*178) - os=-lynxos178 - ;; - -lynx*5) - os=-lynxos5 - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | aarch64 | aarch64_be \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ - | avr | avr32 \ - | be32 | be64 \ - | bfin \ - | c4x | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | epiphany \ - | fido | fr30 | frv \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nios | nios2 | nios2eb | nios2el \ - | ns16k | ns32k \ - | open8 \ - | or1k | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pyramid \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | we32k \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown - ;; - c54x) - basic_machine=tic54x-unknown - ;; - c55x) - basic_machine=tic55x-unknown - ;; - c6x) - basic_machine=tic6x-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - ms1) - basic_machine=mt-unknown - ;; - - strongarm | thumb | xscale) - basic_machine=arm-unknown - ;; - xgate) - basic_machine=$basic_machine-unknown - os=-none - ;; - xscaleeb) - basic_machine=armeb-unknown - ;; - - xscaleel) - basic_machine=armel-unknown - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | aarch64-* | aarch64_be-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ - | microblaze-* | microblazeel-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipsr5900-* | mipsr5900el-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* | nios2eb-* | nios2el-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pyramid-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 - ;; - decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; - i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze*) - basic_machine=microblaze-xilinx - ;; - mingw64) - basic_machine=x86_64-pc - os=-mingw64 - ;; - mingw32) - basic_machine=i386-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - msys) - basic_machine=i386-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pc98) - basic_machine=i386-pc - ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc | ppcbe) basic_machine=powerpc-unknown - ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - os=-rdos - ;; - rdos32) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - s390 | s390-*) - basic_machine=s390-ibm - ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown - ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown - ;; - sde) - basic_machine=mipsisa32-sde - os=-elf - ;; - sei) - basic_machine=mips-sei - os=-seiux - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sh5el) - basic_machine=sh5le-unknown - ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - strongarm-* | thumb-*) - basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; - tile*) - basic_machine=$basic_machine-unknown - os=-linux-gnu - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - tpf) - basic_machine=s390x-ibm - os=-tpf - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xbox) - basic_machine=i686-pc - os=-mingw32 - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - xscale-* | xscalee[bl]-*) - basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - z80-*-coff) - basic_machine=z80-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - romp) - basic_machine=romp-ibm - ;; - mmix) - basic_machine=mmix-knuth - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) - basic_machine=sh-unknown - ;; - sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) - basic_machine=sparc-sun - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; - *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -auroraux) - os=-auroraux - ;; - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ - | -sym* | -kopensolaris* | -plan9* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* | -cegcc* \ - | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ - | -linux-newlib* | -linux-musl* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto-qnx*) - ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` - ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` - ;; - -linux-dietlibc) - os=-linux-dietlibc - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -os400*) - os=-os400 - ;; - -wince*) - os=-wince - ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -atheos*) - os=-atheos - ;; - -syllable*) - os=-syllable - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -nova*) - os=-rtmk-nova - ;; - -ns2 ) - os=-nextstep2 - ;; - -nsk*) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -tpf*) - os=-tpf - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint - ;; - -aros*) - os=-aros - ;; - -zvmoe) - os=-zvmoe - ;; - -dicos*) - os=-dicos - ;; - -nacl*) - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - score-*) - os=-elf - ;; - spu-*) - os=-elf - ;; - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - c4x-* | tic4x-*) - os=-coff - ;; - hexagon-*) - os=-elf - ;; - tic54x-*) - os=-coff - ;; - tic55x-*) - os=-coff - ;; - tic6x-*) - os=-coff - ;; - # This must come before the *-dec entry. - pdp10-*) - os=-tops20 - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - ;; - m68*-cisco) - os=-aout - ;; - mep-*) - os=-elf - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - or1k-*) - os=-elf - ;; - or32-*) - os=-coff - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - *-be) - os=-beos - ;; - *-haiku) - os=-haiku - ;; - *-ibm) - os=-aix - ;; - *-knuth) - os=-mmixware - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f30[01]-fujitsu | f700-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -cnk*|-aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -os400*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -tpf*) - vendor=ibm - ;; - -vxsim* | -vxworks* | -windiss*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - vendor=atari - ;; - -vos*) - vendor=stratus - ;; - esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` - ;; -esac - -echo $basic_machine$os -exit - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/build-aux/install-sh b/build-aux/install-sh deleted file mode 100755 index 377bb8687f..0000000000 --- a/build-aux/install-sh +++ /dev/null @@ -1,527 +0,0 @@ -#!/bin/sh -# install - install a program, script, or datafile - -scriptversion=2011-11-20.07; # UTC - -# This originates from X11R5 (mit/util/scripts/install.sh), which was -# later released in X11R6 (xc/config/util/install.sh) with the -# following copyright and license. -# -# Copyright (C) 1994 X Consortium -# -# 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 -# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN -# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- -# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# -# Except as contained in this notice, the name of the X Consortium shall not -# be used in advertising or otherwise to promote the sale, use or other deal- -# ings in this Software without prior written authorization from the X Consor- -# tium. -# -# -# FSF changes to this file are in the public domain. -# -# Calling this script install-sh is preferred over install.sh, to prevent -# 'make' implicit rules from creating a file called install from it -# when there is no Makefile. -# -# This script is compatible with the BSD install script, but was written -# from scratch. - -nl=' -' -IFS=" "" $nl" - -# set DOITPROG to echo to test this script - -# Don't use :- since 4.3BSD and earlier shells don't like it. -doit=${DOITPROG-} -if test -z "$doit"; then - doit_exec=exec -else - doit_exec=$doit -fi - -# Put in absolute file names if you don't have them in your path; -# or use environment vars. - -chgrpprog=${CHGRPPROG-chgrp} -chmodprog=${CHMODPROG-chmod} -chownprog=${CHOWNPROG-chown} -cmpprog=${CMPPROG-cmp} -cpprog=${CPPROG-cp} -mkdirprog=${MKDIRPROG-mkdir} -mvprog=${MVPROG-mv} -rmprog=${RMPROG-rm} -stripprog=${STRIPPROG-strip} - -posix_glob='?' -initialize_posix_glob=' - test "$posix_glob" != "?" || { - if (set -f) 2>/dev/null; then - posix_glob= - else - posix_glob=: - fi - } -' - -posix_mkdir= - -# Desired mode of installed file. -mode=0755 - -chgrpcmd= -chmodcmd=$chmodprog -chowncmd= -mvcmd=$mvprog -rmcmd="$rmprog -f" -stripcmd= - -src= -dst= -dir_arg= -dst_arg= - -copy_on_change=false -no_target_directory= - -usage="\ -Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE - or: $0 [OPTION]... SRCFILES... DIRECTORY - or: $0 [OPTION]... -t DIRECTORY SRCFILES... - or: $0 [OPTION]... -d DIRECTORIES... - -In the 1st form, copy SRCFILE to DSTFILE. -In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. -In the 4th, create DIRECTORIES. - -Options: - --help display this help and exit. - --version display version info and exit. - - -c (ignored) - -C install only if different (preserve the last data modification time) - -d create directories instead of installing files. - -g GROUP $chgrpprog installed files to GROUP. - -m MODE $chmodprog installed files to MODE. - -o USER $chownprog installed files to USER. - -s $stripprog installed files. - -t DIRECTORY install into DIRECTORY. - -T report an error if DSTFILE is a directory. - -Environment variables override the default commands: - CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG - RMPROG STRIPPROG -" - -while test $# -ne 0; do - case $1 in - -c) ;; - - -C) copy_on_change=true;; - - -d) dir_arg=true;; - - -g) chgrpcmd="$chgrpprog $2" - shift;; - - --help) echo "$usage"; exit $?;; - - -m) mode=$2 - case $mode in - *' '* | *' '* | *' -'* | *'*'* | *'?'* | *'['*) - echo "$0: invalid mode: $mode" >&2 - exit 1;; - esac - shift;; - - -o) chowncmd="$chownprog $2" - shift;; - - -s) stripcmd=$stripprog;; - - -t) dst_arg=$2 - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - shift;; - - -T) no_target_directory=true;; - - --version) echo "$0 $scriptversion"; exit $?;; - - --) shift - break;; - - -*) echo "$0: invalid option: $1" >&2 - exit 1;; - - *) break;; - esac - shift -done - -if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then - # When -d is used, all remaining arguments are directories to create. - # When -t is used, the destination is already specified. - # Otherwise, the last argument is the destination. Remove it from $@. - for arg - do - if test -n "$dst_arg"; then - # $@ is not empty: it contains at least $arg. - set fnord "$@" "$dst_arg" - shift # fnord - fi - shift # arg - dst_arg=$arg - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - done -fi - -if test $# -eq 0; then - if test -z "$dir_arg"; then - echo "$0: no input file specified." >&2 - exit 1 - fi - # It's OK to call 'install-sh -d' without argument. - # This can happen when creating conditional directories. - exit 0 -fi - -if test -z "$dir_arg"; then - do_exit='(exit $ret); exit $ret' - trap "ret=129; $do_exit" 1 - trap "ret=130; $do_exit" 2 - trap "ret=141; $do_exit" 13 - trap "ret=143; $do_exit" 15 - - # Set umask so as not to create temps with too-generous modes. - # However, 'strip' requires both read and write access to temps. - case $mode in - # Optimize common cases. - *644) cp_umask=133;; - *755) cp_umask=22;; - - *[0-7]) - if test -z "$stripcmd"; then - u_plus_rw= - else - u_plus_rw='% 200' - fi - cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; - *) - if test -z "$stripcmd"; then - u_plus_rw= - else - u_plus_rw=,u+rw - fi - cp_umask=$mode$u_plus_rw;; - esac -fi - -for src -do - # Protect names problematic for 'test' and other utilities. - case $src in - -* | [=\(\)!]) src=./$src;; - esac - - if test -n "$dir_arg"; then - dst=$src - dstdir=$dst - test -d "$dstdir" - dstdir_status=$? - else - - # Waiting for this to be detected by the "$cpprog $src $dsttmp" command - # might cause directories to be created, which would be especially bad - # if $src (and thus $dsttmp) contains '*'. - if test ! -f "$src" && test ! -d "$src"; then - echo "$0: $src does not exist." >&2 - exit 1 - fi - - if test -z "$dst_arg"; then - echo "$0: no destination specified." >&2 - exit 1 - fi - dst=$dst_arg - - # If destination is a directory, append the input filename; won't work - # if double slashes aren't ignored. - if test -d "$dst"; then - if test -n "$no_target_directory"; then - echo "$0: $dst_arg: Is a directory" >&2 - exit 1 - fi - dstdir=$dst - dst=$dstdir/`basename "$src"` - dstdir_status=0 - else - # Prefer dirname, but fall back on a substitute if dirname fails. - dstdir=` - (dirname "$dst") 2>/dev/null || - expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$dst" : 'X\(//\)[^/]' \| \ - X"$dst" : 'X\(//\)$' \| \ - X"$dst" : 'X\(/\)' \| . 2>/dev/null || - echo X"$dst" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q' - ` - - test -d "$dstdir" - dstdir_status=$? - fi - fi - - obsolete_mkdir_used=false - - if test $dstdir_status != 0; then - case $posix_mkdir in - '') - # Create intermediate dirs using mode 755 as modified by the umask. - # This is like FreeBSD 'install' as of 1997-10-28. - umask=`umask` - case $stripcmd.$umask in - # Optimize common cases. - *[2367][2367]) mkdir_umask=$umask;; - .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; - - *[0-7]) - mkdir_umask=`expr $umask + 22 \ - - $umask % 100 % 40 + $umask % 20 \ - - $umask % 10 % 4 + $umask % 2 - `;; - *) mkdir_umask=$umask,go-w;; - esac - - # With -d, create the new directory with the user-specified mode. - # Otherwise, rely on $mkdir_umask. - if test -n "$dir_arg"; then - mkdir_mode=-m$mode - else - mkdir_mode= - fi - - posix_mkdir=false - case $umask in - *[123567][0-7][0-7]) - # POSIX mkdir -p sets u+wx bits regardless of umask, which - # is incompatible with FreeBSD 'install' when (umask & 300) != 0. - ;; - *) - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 - - if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/d" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null - fi - trap '' 0;; - esac;; - esac - - if - $posix_mkdir && ( - umask $mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" - ) - then : - else - - # The umask is ridiculous, or mkdir does not conform to POSIX, - # or it failed possibly due to a race condition. Create the - # directory the slow way, step by step, checking for races as we go. - - case $dstdir in - /*) prefix='/';; - [-=\(\)!]*) prefix='./';; - *) prefix='';; - esac - - eval "$initialize_posix_glob" - - oIFS=$IFS - IFS=/ - $posix_glob set -f - set fnord $dstdir - shift - $posix_glob set +f - IFS=$oIFS - - prefixes= - - for d - do - test X"$d" = X && continue - - prefix=$prefix$d - if test -d "$prefix"; then - prefixes= - else - if $posix_mkdir; then - (umask=$mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break - # Don't fail if two instances are running concurrently. - test -d "$prefix" || exit 1 - else - case $prefix in - *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; - *) qprefix=$prefix;; - esac - prefixes="$prefixes '$qprefix'" - fi - fi - prefix=$prefix/ - done - - if test -n "$prefixes"; then - # Don't fail if two instances are running concurrently. - (umask $mkdir_umask && - eval "\$doit_exec \$mkdirprog $prefixes") || - test -d "$dstdir" || exit 1 - obsolete_mkdir_used=true - fi - fi - fi - - if test -n "$dir_arg"; then - { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && - { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && - { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || - test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 - else - - # Make a couple of temp file names in the proper directory. - dsttmp=$dstdir/_inst.$$_ - rmtmp=$dstdir/_rm.$$_ - - # Trap to clean up those temp files at exit. - trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 - - # Copy the file name to the temp name. - (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && - - # and set any options; do chmod last to preserve setuid bits. - # - # If any of these fail, we abort the whole thing. If we want to - # ignore errors from any of these, just make sure not to ignore - # errors from the above "$doit $cpprog $src $dsttmp" command. - # - { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && - { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && - { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && - { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && - - # If -C, don't bother to copy if it wouldn't change the file. - if $copy_on_change && - old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && - new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && - - eval "$initialize_posix_glob" && - $posix_glob set -f && - set X $old && old=:$2:$4:$5:$6 && - set X $new && new=:$2:$4:$5:$6 && - $posix_glob set +f && - - test "$old" = "$new" && - $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 - then - rm -f "$dsttmp" - else - # Rename the file to the real destination. - $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || - - # The rename failed, perhaps because mv can't rename something else - # to itself, or perhaps because mv is so ancient that it does not - # support -f. - { - # Now remove or move aside any old file at destination location. - # We try this two ways since rm can't unlink itself on some - # systems and the destination file might be busy for other - # reasons. In this case, the final cleanup might fail but the new - # file should still install successfully. - { - test ! -f "$dst" || - $doit $rmcmd -f "$dst" 2>/dev/null || - { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } - } || - { echo "$0: cannot unlink or rename $dst" >&2 - (exit 1); exit 1 - } - } && - - # Now rename the file to the real destination. - $doit $mvcmd "$dsttmp" "$dst" - } - fi || exit 1 - - trap '' 0 - fi -done - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" -# time-stamp-end: "; # UTC" -# End: diff --git a/build-aux/ltmain.sh b/build-aux/ltmain.sh deleted file mode 100644 index 63ae69dc6f..0000000000 --- a/build-aux/ltmain.sh +++ /dev/null @@ -1,9655 +0,0 @@ - -# libtool (GNU libtool) 2.4.2 -# Written by Gordon Matzigkeit , 1996 - -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, -# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. -# This is free software; see the source for copying conditions. There is NO -# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -# GNU Libtool is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# As a special exception to the GNU General Public License, -# if you distribute this file as part of a program or library that -# is built using GNU Libtool, you may include this file under the -# same distribution terms that you use for the rest of that program. -# -# GNU Libtool is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Libtool; see the file COPYING. If not, a copy -# can be downloaded from http://www.gnu.org/licenses/gpl.html, -# or obtained by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -# Usage: $progname [OPTION]... [MODE-ARG]... -# -# Provide generalized library-building support services. -# -# --config show all configuration variables -# --debug enable verbose shell tracing -# -n, --dry-run display commands without modifying any files -# --features display basic configuration information and exit -# --mode=MODE use operation mode MODE -# --preserve-dup-deps don't remove duplicate dependency libraries -# --quiet, --silent don't print informational messages -# --no-quiet, --no-silent -# print informational messages (default) -# --no-warn don't display warning messages -# --tag=TAG use configuration variables from tag TAG -# -v, --verbose print more informational messages than default -# --no-verbose don't print the extra informational messages -# --version print version information -# -h, --help, --help-all print short, long, or detailed help message -# -# MODE must be one of the following: -# -# clean remove files from the build directory -# compile compile a source file into a libtool object -# execute automatically set library path, then run a program -# finish complete the installation of libtool libraries -# install install libraries or executables -# link create a library or an executable -# uninstall remove libraries from an installed directory -# -# MODE-ARGS vary depending on the MODE. When passed as first option, -# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. -# Try `$progname --help --mode=MODE' for a more detailed description of MODE. -# -# When reporting a bug, please describe a test case to reproduce it and -# include the following information: -# -# host-triplet: $host -# shell: $SHELL -# compiler: $LTCC -# compiler flags: $LTCFLAGS -# linker: $LD (gnu? $with_gnu_ld) -# $progname: (GNU libtool) 2.4.2 -# automake: $automake_version -# autoconf: $autoconf_version -# -# Report bugs to . -# GNU libtool home page: . -# General help using GNU software: . - -PROGRAM=libtool -PACKAGE=libtool -VERSION=2.4.2 -TIMESTAMP="" -package_revision=1.3337 - -# Be Bourne compatible -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac -fi -BIN_SH=xpg4; export BIN_SH # for Tru64 -DUALCASE=1; export DUALCASE # for MKS sh - -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -$1 -_LTECHO_EOF' -} - -# NLS nuisances: We save the old values to restore during execute mode. -lt_user_locale= -lt_safe_locale= -for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES -do - eval "if test \"\${$lt_var+set}\" = set; then - save_$lt_var=\$$lt_var - $lt_var=C - export $lt_var - lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" - lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" - fi" -done -LC_ALL=C -LANGUAGE=C -export LANGUAGE LC_ALL - -$lt_unset CDPATH - - -# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh -# is ksh but when the shell is invoked as "sh" and the current value of -# the _XPG environment variable is not equal to 1 (one), the special -# positional parameter $0, within a function call, is the name of the -# function. -progpath="$0" - - - -: ${CP="cp -f"} -test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} -: ${MAKE="make"} -: ${MKDIR="mkdir"} -: ${MV="mv -f"} -: ${RM="rm -f"} -: ${SHELL="${CONFIG_SHELL-/bin/sh}"} -: ${Xsed="$SED -e 1s/^X//"} - -# Global variables: -EXIT_SUCCESS=0 -EXIT_FAILURE=1 -EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. -EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. - -exit_status=$EXIT_SUCCESS - -# Make sure IFS has a sensible default -lt_nl=' -' -IFS=" $lt_nl" - -dirname="s,/[^/]*$,," -basename="s,^.*/,," - -# func_dirname file append nondir_replacement -# Compute the dirname of FILE. If nonempty, add APPEND to the result, -# otherwise set result to NONDIR_REPLACEMENT. -func_dirname () -{ - func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` - if test "X$func_dirname_result" = "X${1}"; then - func_dirname_result="${3}" - else - func_dirname_result="$func_dirname_result${2}" - fi -} # func_dirname may be replaced by extended shell implementation - - -# func_basename file -func_basename () -{ - func_basename_result=`$ECHO "${1}" | $SED "$basename"` -} # func_basename may be replaced by extended shell implementation - - -# func_dirname_and_basename file append nondir_replacement -# perform func_basename and func_dirname in a single function -# call: -# dirname: Compute the dirname of FILE. If nonempty, -# add APPEND to the result, otherwise set result -# to NONDIR_REPLACEMENT. -# value returned in "$func_dirname_result" -# basename: Compute filename of FILE. -# value retuned in "$func_basename_result" -# Implementation must be kept synchronized with func_dirname -# and func_basename. For efficiency, we do not delegate to -# those functions but instead duplicate the functionality here. -func_dirname_and_basename () -{ - # Extract subdirectory from the argument. - func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` - if test "X$func_dirname_result" = "X${1}"; then - func_dirname_result="${3}" - else - func_dirname_result="$func_dirname_result${2}" - fi - func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` -} # func_dirname_and_basename may be replaced by extended shell implementation - - -# func_stripname prefix suffix name -# strip PREFIX and SUFFIX off of NAME. -# PREFIX and SUFFIX must not contain globbing or regex special -# characters, hashes, percent signs, but SUFFIX may contain a leading -# dot (in which case that matches only a dot). -# func_strip_suffix prefix name -func_stripname () -{ - case ${2} in - .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; - *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; - esac -} # func_stripname may be replaced by extended shell implementation - - -# These SED scripts presuppose an absolute path with a trailing slash. -pathcar='s,^/\([^/]*\).*$,\1,' -pathcdr='s,^/[^/]*,,' -removedotparts=':dotsl - s@/\./@/@g - t dotsl - s,/\.$,/,' -collapseslashes='s@/\{1,\}@/@g' -finalslash='s,/*$,/,' - -# func_normal_abspath PATH -# Remove doubled-up and trailing slashes, "." path components, -# and cancel out any ".." path components in PATH after making -# it an absolute path. -# value returned in "$func_normal_abspath_result" -func_normal_abspath () -{ - # Start from root dir and reassemble the path. - func_normal_abspath_result= - func_normal_abspath_tpath=$1 - func_normal_abspath_altnamespace= - case $func_normal_abspath_tpath in - "") - # Empty path, that just means $cwd. - func_stripname '' '/' "`pwd`" - func_normal_abspath_result=$func_stripname_result - return - ;; - # The next three entries are used to spot a run of precisely - # two leading slashes without using negated character classes; - # we take advantage of case's first-match behaviour. - ///*) - # Unusual form of absolute path, do nothing. - ;; - //*) - # Not necessarily an ordinary path; POSIX reserves leading '//' - # and for example Cygwin uses it to access remote file shares - # over CIFS/SMB, so we conserve a leading double slash if found. - func_normal_abspath_altnamespace=/ - ;; - /*) - # Absolute path, do nothing. - ;; - *) - # Relative path, prepend $cwd. - func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath - ;; - esac - # Cancel out all the simple stuff to save iterations. We also want - # the path to end with a slash for ease of parsing, so make sure - # there is one (and only one) here. - func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ - -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` - while :; do - # Processed it all yet? - if test "$func_normal_abspath_tpath" = / ; then - # If we ascended to the root using ".." the result may be empty now. - if test -z "$func_normal_abspath_result" ; then - func_normal_abspath_result=/ - fi - break - fi - func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ - -e "$pathcar"` - func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ - -e "$pathcdr"` - # Figure out what to do with it - case $func_normal_abspath_tcomponent in - "") - # Trailing empty path component, ignore it. - ;; - ..) - # Parent dir; strip last assembled component from result. - func_dirname "$func_normal_abspath_result" - func_normal_abspath_result=$func_dirname_result - ;; - *) - # Actual path component, append it. - func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent - ;; - esac - done - # Restore leading double-slash if one was found on entry. - func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result -} - -# func_relative_path SRCDIR DSTDIR -# generates a relative path from SRCDIR to DSTDIR, with a trailing -# slash if non-empty, suitable for immediately appending a filename -# without needing to append a separator. -# value returned in "$func_relative_path_result" -func_relative_path () -{ - func_relative_path_result= - func_normal_abspath "$1" - func_relative_path_tlibdir=$func_normal_abspath_result - func_normal_abspath "$2" - func_relative_path_tbindir=$func_normal_abspath_result - - # Ascend the tree starting from libdir - while :; do - # check if we have found a prefix of bindir - case $func_relative_path_tbindir in - $func_relative_path_tlibdir) - # found an exact match - func_relative_path_tcancelled= - break - ;; - $func_relative_path_tlibdir*) - # found a matching prefix - func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" - func_relative_path_tcancelled=$func_stripname_result - if test -z "$func_relative_path_result"; then - func_relative_path_result=. - fi - break - ;; - *) - func_dirname $func_relative_path_tlibdir - func_relative_path_tlibdir=${func_dirname_result} - if test "x$func_relative_path_tlibdir" = x ; then - # Have to descend all the way to the root! - func_relative_path_result=../$func_relative_path_result - func_relative_path_tcancelled=$func_relative_path_tbindir - break - fi - func_relative_path_result=../$func_relative_path_result - ;; - esac - done - - # Now calculate path; take care to avoid doubling-up slashes. - func_stripname '' '/' "$func_relative_path_result" - func_relative_path_result=$func_stripname_result - func_stripname '/' '/' "$func_relative_path_tcancelled" - if test "x$func_stripname_result" != x ; then - func_relative_path_result=${func_relative_path_result}/${func_stripname_result} - fi - - # Normalisation. If bindir is libdir, return empty string, - # else relative path ending with a slash; either way, target - # file name can be directly appended. - if test ! -z "$func_relative_path_result"; then - func_stripname './' '' "$func_relative_path_result/" - func_relative_path_result=$func_stripname_result - fi -} - -# The name of this program: -func_dirname_and_basename "$progpath" -progname=$func_basename_result - -# Make sure we have an absolute path for reexecution: -case $progpath in - [\\/]*|[A-Za-z]:\\*) ;; - *[\\/]*) - progdir=$func_dirname_result - progdir=`cd "$progdir" && pwd` - progpath="$progdir/$progname" - ;; - *) - save_IFS="$IFS" - IFS=${PATH_SEPARATOR-:} - for progdir in $PATH; do - IFS="$save_IFS" - test -x "$progdir/$progname" && break - done - IFS="$save_IFS" - test -n "$progdir" || progdir=`pwd` - progpath="$progdir/$progname" - ;; -esac - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed="${SED}"' -e 1s/^X//' -sed_quote_subst='s/\([`"$\\]\)/\\\1/g' - -# Same as above, but do not quote variable references. -double_quote_subst='s/\(["`\\]\)/\\\1/g' - -# Sed substitution that turns a string into a regex matching for the -# string literally. -sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' - -# Sed substitution that converts a w32 file name or path -# which contains forward slashes, into one that contains -# (escaped) backslashes. A very naive implementation. -lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' - -# Re-`\' parameter expansions in output of double_quote_subst that were -# `\'-ed in input to the same. If an odd number of `\' preceded a '$' -# in input to double_quote_subst, that '$' was protected from expansion. -# Since each input `\' is now two `\'s, look for any number of runs of -# four `\'s followed by two `\'s and then a '$'. `\' that '$'. -bs='\\' -bs2='\\\\' -bs4='\\\\\\\\' -dollar='\$' -sed_double_backslash="\ - s/$bs4/&\\ -/g - s/^$bs2$dollar/$bs&/ - s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g - s/\n//g" - -# Standard options: -opt_dry_run=false -opt_help=false -opt_quiet=false -opt_verbose=false -opt_warning=: - -# func_echo arg... -# Echo program name prefixed message, along with the current mode -# name if it has been set yet. -func_echo () -{ - $ECHO "$progname: ${opt_mode+$opt_mode: }$*" -} - -# func_verbose arg... -# Echo program name prefixed message in verbose mode only. -func_verbose () -{ - $opt_verbose && func_echo ${1+"$@"} - - # A bug in bash halts the script if the last line of a function - # fails when set -e is in force, so we need another command to - # work around that: - : -} - -# func_echo_all arg... -# Invoke $ECHO with all args, space-separated. -func_echo_all () -{ - $ECHO "$*" -} - -# func_error arg... -# Echo program name prefixed message to standard error. -func_error () -{ - $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 -} - -# func_warning arg... -# Echo program name prefixed warning message to standard error. -func_warning () -{ - $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 - - # bash bug again: - : -} - -# func_fatal_error arg... -# Echo program name prefixed message to standard error, and exit. -func_fatal_error () -{ - func_error ${1+"$@"} - exit $EXIT_FAILURE -} - -# func_fatal_help arg... -# Echo program name prefixed message to standard error, followed by -# a help hint, and exit. -func_fatal_help () -{ - func_error ${1+"$@"} - func_fatal_error "$help" -} -help="Try \`$progname --help' for more information." ## default - - -# func_grep expression filename -# Check whether EXPRESSION matches any line of FILENAME, without output. -func_grep () -{ - $GREP "$1" "$2" >/dev/null 2>&1 -} - - -# func_mkdir_p directory-path -# Make sure the entire path to DIRECTORY-PATH is available. -func_mkdir_p () -{ - my_directory_path="$1" - my_dir_list= - - if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then - - # Protect directory names starting with `-' - case $my_directory_path in - -*) my_directory_path="./$my_directory_path" ;; - esac - - # While some portion of DIR does not yet exist... - while test ! -d "$my_directory_path"; do - # ...make a list in topmost first order. Use a colon delimited - # list incase some portion of path contains whitespace. - my_dir_list="$my_directory_path:$my_dir_list" - - # If the last portion added has no slash in it, the list is done - case $my_directory_path in */*) ;; *) break ;; esac - - # ...otherwise throw away the child directory and loop - my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` - done - my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` - - save_mkdir_p_IFS="$IFS"; IFS=':' - for my_dir in $my_dir_list; do - IFS="$save_mkdir_p_IFS" - # mkdir can fail with a `File exist' error if two processes - # try to create one of the directories concurrently. Don't - # stop in that case! - $MKDIR "$my_dir" 2>/dev/null || : - done - IFS="$save_mkdir_p_IFS" - - # Bail out if we (or some other process) failed to create a directory. - test -d "$my_directory_path" || \ - func_fatal_error "Failed to create \`$1'" - fi -} - - -# func_mktempdir [string] -# Make a temporary directory that won't clash with other running -# libtool processes, and avoids race conditions if possible. If -# given, STRING is the basename for that directory. -func_mktempdir () -{ - my_template="${TMPDIR-/tmp}/${1-$progname}" - - if test "$opt_dry_run" = ":"; then - # Return a directory name, but don't create it in dry-run mode - my_tmpdir="${my_template}-$$" - else - - # If mktemp works, use that first and foremost - my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` - - if test ! -d "$my_tmpdir"; then - # Failing that, at least try and use $RANDOM to avoid a race - my_tmpdir="${my_template}-${RANDOM-0}$$" - - save_mktempdir_umask=`umask` - umask 0077 - $MKDIR "$my_tmpdir" - umask $save_mktempdir_umask - fi - - # If we're not in dry-run mode, bomb out on failure - test -d "$my_tmpdir" || \ - func_fatal_error "cannot create temporary directory \`$my_tmpdir'" - fi - - $ECHO "$my_tmpdir" -} - - -# func_quote_for_eval arg -# Aesthetically quote ARG to be evaled later. -# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT -# is double-quoted, suitable for a subsequent eval, whereas -# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters -# which are still active within double quotes backslashified. -func_quote_for_eval () -{ - case $1 in - *[\\\`\"\$]*) - func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; - *) - func_quote_for_eval_unquoted_result="$1" ;; - esac - - case $func_quote_for_eval_unquoted_result in - # Double-quote args containing shell metacharacters to delay - # word splitting, command substitution and and variable - # expansion for a subsequent eval. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" - ;; - *) - func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" - esac -} - - -# func_quote_for_expand arg -# Aesthetically quote ARG to be evaled later; same as above, -# but do not quote variable references. -func_quote_for_expand () -{ - case $1 in - *[\\\`\"]*) - my_arg=`$ECHO "$1" | $SED \ - -e "$double_quote_subst" -e "$sed_double_backslash"` ;; - *) - my_arg="$1" ;; - esac - - case $my_arg in - # Double-quote args containing shell metacharacters to delay - # word splitting and command substitution for a subsequent eval. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - my_arg="\"$my_arg\"" - ;; - esac - - func_quote_for_expand_result="$my_arg" -} - - -# func_show_eval cmd [fail_exp] -# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is -# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP -# is given, then evaluate it. -func_show_eval () -{ - my_cmd="$1" - my_fail_exp="${2-:}" - - ${opt_silent-false} || { - func_quote_for_expand "$my_cmd" - eval "func_echo $func_quote_for_expand_result" - } - - if ${opt_dry_run-false}; then :; else - eval "$my_cmd" - my_status=$? - if test "$my_status" -eq 0; then :; else - eval "(exit $my_status); $my_fail_exp" - fi - fi -} - - -# func_show_eval_locale cmd [fail_exp] -# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is -# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP -# is given, then evaluate it. Use the saved locale for evaluation. -func_show_eval_locale () -{ - my_cmd="$1" - my_fail_exp="${2-:}" - - ${opt_silent-false} || { - func_quote_for_expand "$my_cmd" - eval "func_echo $func_quote_for_expand_result" - } - - if ${opt_dry_run-false}; then :; else - eval "$lt_user_locale - $my_cmd" - my_status=$? - eval "$lt_safe_locale" - if test "$my_status" -eq 0; then :; else - eval "(exit $my_status); $my_fail_exp" - fi - fi -} - -# func_tr_sh -# Turn $1 into a string suitable for a shell variable name. -# Result is stored in $func_tr_sh_result. All characters -# not in the set a-zA-Z0-9_ are replaced with '_'. Further, -# if $1 begins with a digit, a '_' is prepended as well. -func_tr_sh () -{ - case $1 in - [0-9]* | *[!a-zA-Z0-9_]*) - func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` - ;; - * ) - func_tr_sh_result=$1 - ;; - esac -} - - -# func_version -# Echo version message to standard output and exit. -func_version () -{ - $opt_debug - - $SED -n '/(C)/!b go - :more - /\./!{ - N - s/\n# / / - b more - } - :go - /^# '$PROGRAM' (GNU /,/# warranty; / { - s/^# // - s/^# *$// - s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ - p - }' < "$progpath" - exit $? -} - -# func_usage -# Echo short help message to standard output and exit. -func_usage () -{ - $opt_debug - - $SED -n '/^# Usage:/,/^# *.*--help/ { - s/^# // - s/^# *$// - s/\$progname/'$progname'/ - p - }' < "$progpath" - echo - $ECHO "run \`$progname --help | more' for full usage" - exit $? -} - -# func_help [NOEXIT] -# Echo long help message to standard output and exit, -# unless 'noexit' is passed as argument. -func_help () -{ - $opt_debug - - $SED -n '/^# Usage:/,/# Report bugs to/ { - :print - s/^# // - s/^# *$// - s*\$progname*'$progname'* - s*\$host*'"$host"'* - s*\$SHELL*'"$SHELL"'* - s*\$LTCC*'"$LTCC"'* - s*\$LTCFLAGS*'"$LTCFLAGS"'* - s*\$LD*'"$LD"'* - s/\$with_gnu_ld/'"$with_gnu_ld"'/ - s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ - s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ - p - d - } - /^# .* home page:/b print - /^# General help using/b print - ' < "$progpath" - ret=$? - if test -z "$1"; then - exit $ret - fi -} - -# func_missing_arg argname -# Echo program name prefixed message to standard error and set global -# exit_cmd. -func_missing_arg () -{ - $opt_debug - - func_error "missing argument for $1." - exit_cmd=exit -} - - -# func_split_short_opt shortopt -# Set func_split_short_opt_name and func_split_short_opt_arg shell -# variables after splitting SHORTOPT after the 2nd character. -func_split_short_opt () -{ - my_sed_short_opt='1s/^\(..\).*$/\1/;q' - my_sed_short_rest='1s/^..\(.*\)$/\1/;q' - - func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` - func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` -} # func_split_short_opt may be replaced by extended shell implementation - - -# func_split_long_opt longopt -# Set func_split_long_opt_name and func_split_long_opt_arg shell -# variables after splitting LONGOPT at the `=' sign. -func_split_long_opt () -{ - my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' - my_sed_long_arg='1s/^--[^=]*=//' - - func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` - func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` -} # func_split_long_opt may be replaced by extended shell implementation - -exit_cmd=: - - - - - -magic="%%%MAGIC variable%%%" -magic_exe="%%%MAGIC EXE variable%%%" - -# Global variables. -nonopt= -preserve_args= -lo2o="s/\\.lo\$/.${objext}/" -o2lo="s/\\.${objext}\$/.lo/" -extracted_archives= -extracted_serial=0 - -# If this variable is set in any of the actions, the command in it -# will be execed at the end. This prevents here-documents from being -# left over by shells. -exec_cmd= - -# func_append var value -# Append VALUE to the end of shell variable VAR. -func_append () -{ - eval "${1}=\$${1}\${2}" -} # func_append may be replaced by extended shell implementation - -# func_append_quoted var value -# Quote VALUE and append to the end of shell variable VAR, separated -# by a space. -func_append_quoted () -{ - func_quote_for_eval "${2}" - eval "${1}=\$${1}\\ \$func_quote_for_eval_result" -} # func_append_quoted may be replaced by extended shell implementation - - -# func_arith arithmetic-term... -func_arith () -{ - func_arith_result=`expr "${@}"` -} # func_arith may be replaced by extended shell implementation - - -# func_len string -# STRING may not start with a hyphen. -func_len () -{ - func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` -} # func_len may be replaced by extended shell implementation - - -# func_lo2o object -func_lo2o () -{ - func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` -} # func_lo2o may be replaced by extended shell implementation - - -# func_xform libobj-or-source -func_xform () -{ - func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` -} # func_xform may be replaced by extended shell implementation - - -# func_fatal_configuration arg... -# Echo program name prefixed message to standard error, followed by -# a configuration failure hint, and exit. -func_fatal_configuration () -{ - func_error ${1+"$@"} - func_error "See the $PACKAGE documentation for more information." - func_fatal_error "Fatal configuration error." -} - - -# func_config -# Display the configuration for all the tags in this script. -func_config () -{ - re_begincf='^# ### BEGIN LIBTOOL' - re_endcf='^# ### END LIBTOOL' - - # Default configuration. - $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" - - # Now print the configurations for the tags. - for tagname in $taglist; do - $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" - done - - exit $? -} - -# func_features -# Display the features supported by this script. -func_features () -{ - echo "host: $host" - if test "$build_libtool_libs" = yes; then - echo "enable shared libraries" - else - echo "disable shared libraries" - fi - if test "$build_old_libs" = yes; then - echo "enable static libraries" - else - echo "disable static libraries" - fi - - exit $? -} - -# func_enable_tag tagname -# Verify that TAGNAME is valid, and either flag an error and exit, or -# enable the TAGNAME tag. We also add TAGNAME to the global $taglist -# variable here. -func_enable_tag () -{ - # Global variable: - tagname="$1" - - re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" - re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" - sed_extractcf="/$re_begincf/,/$re_endcf/p" - - # Validate tagname. - case $tagname in - *[!-_A-Za-z0-9,/]*) - func_fatal_error "invalid tag name: $tagname" - ;; - esac - - # Don't test for the "default" C tag, as we know it's - # there but not specially marked. - case $tagname in - CC) ;; - *) - if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then - taglist="$taglist $tagname" - - # Evaluate the configuration. Be careful to quote the path - # and the sed script, to avoid splitting on whitespace, but - # also don't use non-portable quotes within backquotes within - # quotes we have to do it in 2 steps: - extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` - eval "$extractedcf" - else - func_error "ignoring unknown tag $tagname" - fi - ;; - esac -} - -# func_check_version_match -# Ensure that we are using m4 macros, and libtool script from the same -# release of libtool. -func_check_version_match () -{ - if test "$package_revision" != "$macro_revision"; then - if test "$VERSION" != "$macro_version"; then - if test -z "$macro_version"; then - cat >&2 <<_LT_EOF -$progname: Version mismatch error. This is $PACKAGE $VERSION, but the -$progname: definition of this LT_INIT comes from an older release. -$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION -$progname: and run autoconf again. -_LT_EOF - else - cat >&2 <<_LT_EOF -$progname: Version mismatch error. This is $PACKAGE $VERSION, but the -$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. -$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION -$progname: and run autoconf again. -_LT_EOF - fi - else - cat >&2 <<_LT_EOF -$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, -$progname: but the definition of this LT_INIT comes from revision $macro_revision. -$progname: You should recreate aclocal.m4 with macros from revision $package_revision -$progname: of $PACKAGE $VERSION and run autoconf again. -_LT_EOF - fi - - exit $EXIT_MISMATCH - fi -} - - -# Shorthand for --mode=foo, only valid as the first argument -case $1 in -clean|clea|cle|cl) - shift; set dummy --mode clean ${1+"$@"}; shift - ;; -compile|compil|compi|comp|com|co|c) - shift; set dummy --mode compile ${1+"$@"}; shift - ;; -execute|execut|execu|exec|exe|ex|e) - shift; set dummy --mode execute ${1+"$@"}; shift - ;; -finish|finis|fini|fin|fi|f) - shift; set dummy --mode finish ${1+"$@"}; shift - ;; -install|instal|insta|inst|ins|in|i) - shift; set dummy --mode install ${1+"$@"}; shift - ;; -link|lin|li|l) - shift; set dummy --mode link ${1+"$@"}; shift - ;; -uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) - shift; set dummy --mode uninstall ${1+"$@"}; shift - ;; -esac - - - -# Option defaults: -opt_debug=: -opt_dry_run=false -opt_config=false -opt_preserve_dup_deps=false -opt_features=false -opt_finish=false -opt_help=false -opt_help_all=false -opt_silent=: -opt_warning=: -opt_verbose=: -opt_silent=false -opt_verbose=false - - -# Parse options once, thoroughly. This comes as soon as possible in the -# script to make things like `--version' happen as quickly as we can. -{ - # this just eases exit handling - while test $# -gt 0; do - opt="$1" - shift - case $opt in - --debug|-x) opt_debug='set -x' - func_echo "enabling shell trace mode" - $opt_debug - ;; - --dry-run|--dryrun|-n) - opt_dry_run=: - ;; - --config) - opt_config=: -func_config - ;; - --dlopen|-dlopen) - optarg="$1" - opt_dlopen="${opt_dlopen+$opt_dlopen -}$optarg" - shift - ;; - --preserve-dup-deps) - opt_preserve_dup_deps=: - ;; - --features) - opt_features=: -func_features - ;; - --finish) - opt_finish=: -set dummy --mode finish ${1+"$@"}; shift - ;; - --help) - opt_help=: - ;; - --help-all) - opt_help_all=: -opt_help=': help-all' - ;; - --mode) - test $# = 0 && func_missing_arg $opt && break - optarg="$1" - opt_mode="$optarg" -case $optarg in - # Valid mode arguments: - clean|compile|execute|finish|install|link|relink|uninstall) ;; - - # Catch anything else as an error - *) func_error "invalid argument for $opt" - exit_cmd=exit - break - ;; -esac - shift - ;; - --no-silent|--no-quiet) - opt_silent=false -func_append preserve_args " $opt" - ;; - --no-warning|--no-warn) - opt_warning=false -func_append preserve_args " $opt" - ;; - --no-verbose) - opt_verbose=false -func_append preserve_args " $opt" - ;; - --silent|--quiet) - opt_silent=: -func_append preserve_args " $opt" - opt_verbose=false - ;; - --verbose|-v) - opt_verbose=: -func_append preserve_args " $opt" -opt_silent=false - ;; - --tag) - test $# = 0 && func_missing_arg $opt && break - optarg="$1" - opt_tag="$optarg" -func_append preserve_args " $opt $optarg" -func_enable_tag "$optarg" - shift - ;; - - -\?|-h) func_usage ;; - --help) func_help ;; - --version) func_version ;; - - # Separate optargs to long options: - --*=*) - func_split_long_opt "$opt" - set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} - shift - ;; - - # Separate non-argument short options: - -\?*|-h*|-n*|-v*) - func_split_short_opt "$opt" - set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} - shift - ;; - - --) break ;; - -*) func_fatal_help "unrecognized option \`$opt'" ;; - *) set dummy "$opt" ${1+"$@"}; shift; break ;; - esac - done - - # Validate options: - - # save first non-option argument - if test "$#" -gt 0; then - nonopt="$opt" - shift - fi - - # preserve --debug - test "$opt_debug" = : || func_append preserve_args " --debug" - - case $host in - *cygwin* | *mingw* | *pw32* | *cegcc*) - # don't eliminate duplications in $postdeps and $predeps - opt_duplicate_compiler_generated_deps=: - ;; - *) - opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps - ;; - esac - - $opt_help || { - # Sanity checks first: - func_check_version_match - - if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then - func_fatal_configuration "not configured to build any kind of library" - fi - - # Darwin sucks - eval std_shrext=\"$shrext_cmds\" - - # Only execute mode is allowed to have -dlopen flags. - if test -n "$opt_dlopen" && test "$opt_mode" != execute; then - func_error "unrecognized option \`-dlopen'" - $ECHO "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Change the help message to a mode-specific one. - generic_help="$help" - help="Try \`$progname --help --mode=$opt_mode' for more information." - } - - - # Bail if the options were screwed - $exit_cmd $EXIT_FAILURE -} - - - - -## ----------- ## -## Main. ## -## ----------- ## - -# func_lalib_p file -# True iff FILE is a libtool `.la' library or `.lo' object file. -# This function is only a basic sanity check; it will hardly flush out -# determined imposters. -func_lalib_p () -{ - test -f "$1" && - $SED -e 4q "$1" 2>/dev/null \ - | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 -} - -# func_lalib_unsafe_p file -# True iff FILE is a libtool `.la' library or `.lo' object file. -# This function implements the same check as func_lalib_p without -# resorting to external programs. To this end, it redirects stdin and -# closes it afterwards, without saving the original file descriptor. -# As a safety measure, use it only where a negative result would be -# fatal anyway. Works if `file' does not exist. -func_lalib_unsafe_p () -{ - lalib_p=no - if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then - for lalib_p_l in 1 2 3 4 - do - read lalib_p_line - case "$lalib_p_line" in - \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; - esac - done - exec 0<&5 5<&- - fi - test "$lalib_p" = yes -} - -# func_ltwrapper_script_p file -# True iff FILE is a libtool wrapper script -# This function is only a basic sanity check; it will hardly flush out -# determined imposters. -func_ltwrapper_script_p () -{ - func_lalib_p "$1" -} - -# func_ltwrapper_executable_p file -# True iff FILE is a libtool wrapper executable -# This function is only a basic sanity check; it will hardly flush out -# determined imposters. -func_ltwrapper_executable_p () -{ - func_ltwrapper_exec_suffix= - case $1 in - *.exe) ;; - *) func_ltwrapper_exec_suffix=.exe ;; - esac - $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 -} - -# func_ltwrapper_scriptname file -# Assumes file is an ltwrapper_executable -# uses $file to determine the appropriate filename for a -# temporary ltwrapper_script. -func_ltwrapper_scriptname () -{ - func_dirname_and_basename "$1" "" "." - func_stripname '' '.exe' "$func_basename_result" - func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" -} - -# func_ltwrapper_p file -# True iff FILE is a libtool wrapper script or wrapper executable -# This function is only a basic sanity check; it will hardly flush out -# determined imposters. -func_ltwrapper_p () -{ - func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" -} - - -# func_execute_cmds commands fail_cmd -# Execute tilde-delimited COMMANDS. -# If FAIL_CMD is given, eval that upon failure. -# FAIL_CMD may read-access the current command in variable CMD! -func_execute_cmds () -{ - $opt_debug - save_ifs=$IFS; IFS='~' - for cmd in $1; do - IFS=$save_ifs - eval cmd=\"$cmd\" - func_show_eval "$cmd" "${2-:}" - done - IFS=$save_ifs -} - - -# func_source file -# Source FILE, adding directory component if necessary. -# Note that it is not necessary on cygwin/mingw to append a dot to -# FILE even if both FILE and FILE.exe exist: automatic-append-.exe -# behavior happens only for exec(3), not for open(2)! Also, sourcing -# `FILE.' does not work on cygwin managed mounts. -func_source () -{ - $opt_debug - case $1 in - */* | *\\*) . "$1" ;; - *) . "./$1" ;; - esac -} - - -# func_resolve_sysroot PATH -# Replace a leading = in PATH with a sysroot. Store the result into -# func_resolve_sysroot_result -func_resolve_sysroot () -{ - func_resolve_sysroot_result=$1 - case $func_resolve_sysroot_result in - =*) - func_stripname '=' '' "$func_resolve_sysroot_result" - func_resolve_sysroot_result=$lt_sysroot$func_stripname_result - ;; - esac -} - -# func_replace_sysroot PATH -# If PATH begins with the sysroot, replace it with = and -# store the result into func_replace_sysroot_result. -func_replace_sysroot () -{ - case "$lt_sysroot:$1" in - ?*:"$lt_sysroot"*) - func_stripname "$lt_sysroot" '' "$1" - func_replace_sysroot_result="=$func_stripname_result" - ;; - *) - # Including no sysroot. - func_replace_sysroot_result=$1 - ;; - esac -} - -# func_infer_tag arg -# Infer tagged configuration to use if any are available and -# if one wasn't chosen via the "--tag" command line option. -# Only attempt this if the compiler in the base compile -# command doesn't match the default compiler. -# arg is usually of the form 'gcc ...' -func_infer_tag () -{ - $opt_debug - if test -n "$available_tags" && test -z "$tagname"; then - CC_quoted= - for arg in $CC; do - func_append_quoted CC_quoted "$arg" - done - CC_expanded=`func_echo_all $CC` - CC_quoted_expanded=`func_echo_all $CC_quoted` - case $@ in - # Blanks in the command may have been stripped by the calling shell, - # but not from the CC environment variable when configure was run. - " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ - " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; - # Blanks at the start of $base_compile will cause this to fail - # if we don't check for them as well. - *) - for z in $available_tags; do - if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then - # Evaluate the configuration. - eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" - CC_quoted= - for arg in $CC; do - # Double-quote args containing other shell metacharacters. - func_append_quoted CC_quoted "$arg" - done - CC_expanded=`func_echo_all $CC` - CC_quoted_expanded=`func_echo_all $CC_quoted` - case "$@ " in - " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ - " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) - # The compiler in the base compile command matches - # the one in the tagged configuration. - # Assume this is the tagged configuration we want. - tagname=$z - break - ;; - esac - fi - done - # If $tagname still isn't set, then no tagged configuration - # was found and let the user know that the "--tag" command - # line option must be used. - if test -z "$tagname"; then - func_echo "unable to infer tagged configuration" - func_fatal_error "specify a tag with \`--tag'" -# else -# func_verbose "using $tagname tagged configuration" - fi - ;; - esac - fi -} - - - -# func_write_libtool_object output_name pic_name nonpic_name -# Create a libtool object file (analogous to a ".la" file), -# but don't create it if we're doing a dry run. -func_write_libtool_object () -{ - write_libobj=${1} - if test "$build_libtool_libs" = yes; then - write_lobj=\'${2}\' - else - write_lobj=none - fi - - if test "$build_old_libs" = yes; then - write_oldobj=\'${3}\' - else - write_oldobj=none - fi - - $opt_dry_run || { - cat >${write_libobj}T </dev/null` - if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then - func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | - $SED -e "$lt_sed_naive_backslashify"` - else - func_convert_core_file_wine_to_w32_result= - fi - fi -} -# end: func_convert_core_file_wine_to_w32 - - -# func_convert_core_path_wine_to_w32 ARG -# Helper function used by path conversion functions when $build is *nix, and -# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly -# configured wine environment available, with the winepath program in $build's -# $PATH. Assumes ARG has no leading or trailing path separator characters. -# -# ARG is path to be converted from $build format to win32. -# Result is available in $func_convert_core_path_wine_to_w32_result. -# Unconvertible file (directory) names in ARG are skipped; if no directory names -# are convertible, then the result may be empty. -func_convert_core_path_wine_to_w32 () -{ - $opt_debug - # unfortunately, winepath doesn't convert paths, only file names - func_convert_core_path_wine_to_w32_result="" - if test -n "$1"; then - oldIFS=$IFS - IFS=: - for func_convert_core_path_wine_to_w32_f in $1; do - IFS=$oldIFS - func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" - if test -n "$func_convert_core_file_wine_to_w32_result" ; then - if test -z "$func_convert_core_path_wine_to_w32_result"; then - func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" - else - func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" - fi - fi - done - IFS=$oldIFS - fi -} -# end: func_convert_core_path_wine_to_w32 - - -# func_cygpath ARGS... -# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when -# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) -# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or -# (2), returns the Cygwin file name or path in func_cygpath_result (input -# file name or path is assumed to be in w32 format, as previously converted -# from $build's *nix or MSYS format). In case (3), returns the w32 file name -# or path in func_cygpath_result (input file name or path is assumed to be in -# Cygwin format). Returns an empty string on error. -# -# ARGS are passed to cygpath, with the last one being the file name or path to -# be converted. -# -# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH -# environment variable; do not put it in $PATH. -func_cygpath () -{ - $opt_debug - if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then - func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` - if test "$?" -ne 0; then - # on failure, ensure result is empty - func_cygpath_result= - fi - else - func_cygpath_result= - func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" - fi -} -#end: func_cygpath - - -# func_convert_core_msys_to_w32 ARG -# Convert file name or path ARG from MSYS format to w32 format. Return -# result in func_convert_core_msys_to_w32_result. -func_convert_core_msys_to_w32 () -{ - $opt_debug - # awkward: cmd appends spaces to result - func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | - $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` -} -#end: func_convert_core_msys_to_w32 - - -# func_convert_file_check ARG1 ARG2 -# Verify that ARG1 (a file name in $build format) was converted to $host -# format in ARG2. Otherwise, emit an error message, but continue (resetting -# func_to_host_file_result to ARG1). -func_convert_file_check () -{ - $opt_debug - if test -z "$2" && test -n "$1" ; then - func_error "Could not determine host file name corresponding to" - func_error " \`$1'" - func_error "Continuing, but uninstalled executables may not work." - # Fallback: - func_to_host_file_result="$1" - fi -} -# end func_convert_file_check - - -# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH -# Verify that FROM_PATH (a path in $build format) was converted to $host -# format in TO_PATH. Otherwise, emit an error message, but continue, resetting -# func_to_host_file_result to a simplistic fallback value (see below). -func_convert_path_check () -{ - $opt_debug - if test -z "$4" && test -n "$3"; then - func_error "Could not determine the host path corresponding to" - func_error " \`$3'" - func_error "Continuing, but uninstalled executables may not work." - # Fallback. This is a deliberately simplistic "conversion" and - # should not be "improved". See libtool.info. - if test "x$1" != "x$2"; then - lt_replace_pathsep_chars="s|$1|$2|g" - func_to_host_path_result=`echo "$3" | - $SED -e "$lt_replace_pathsep_chars"` - else - func_to_host_path_result="$3" - fi - fi -} -# end func_convert_path_check - - -# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG -# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT -# and appending REPL if ORIG matches BACKPAT. -func_convert_path_front_back_pathsep () -{ - $opt_debug - case $4 in - $1 ) func_to_host_path_result="$3$func_to_host_path_result" - ;; - esac - case $4 in - $2 ) func_append func_to_host_path_result "$3" - ;; - esac -} -# end func_convert_path_front_back_pathsep - - -################################################## -# $build to $host FILE NAME CONVERSION FUNCTIONS # -################################################## -# invoked via `$to_host_file_cmd ARG' -# -# In each case, ARG is the path to be converted from $build to $host format. -# Result will be available in $func_to_host_file_result. - - -# func_to_host_file ARG -# Converts the file name ARG from $build format to $host format. Return result -# in func_to_host_file_result. -func_to_host_file () -{ - $opt_debug - $to_host_file_cmd "$1" -} -# end func_to_host_file - - -# func_to_tool_file ARG LAZY -# converts the file name ARG from $build format to toolchain format. Return -# result in func_to_tool_file_result. If the conversion in use is listed -# in (the comma separated) LAZY, no conversion takes place. -func_to_tool_file () -{ - $opt_debug - case ,$2, in - *,"$to_tool_file_cmd",*) - func_to_tool_file_result=$1 - ;; - *) - $to_tool_file_cmd "$1" - func_to_tool_file_result=$func_to_host_file_result - ;; - esac -} -# end func_to_tool_file - - -# func_convert_file_noop ARG -# Copy ARG to func_to_host_file_result. -func_convert_file_noop () -{ - func_to_host_file_result="$1" -} -# end func_convert_file_noop - - -# func_convert_file_msys_to_w32 ARG -# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic -# conversion to w32 is not available inside the cwrapper. Returns result in -# func_to_host_file_result. -func_convert_file_msys_to_w32 () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - func_convert_core_msys_to_w32 "$1" - func_to_host_file_result="$func_convert_core_msys_to_w32_result" - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_msys_to_w32 - - -# func_convert_file_cygwin_to_w32 ARG -# Convert file name ARG from Cygwin to w32 format. Returns result in -# func_to_host_file_result. -func_convert_file_cygwin_to_w32 () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - # because $build is cygwin, we call "the" cygpath in $PATH; no need to use - # LT_CYGPATH in this case. - func_to_host_file_result=`cygpath -m "$1"` - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_cygwin_to_w32 - - -# func_convert_file_nix_to_w32 ARG -# Convert file name ARG from *nix to w32 format. Requires a wine environment -# and a working winepath. Returns result in func_to_host_file_result. -func_convert_file_nix_to_w32 () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - func_convert_core_file_wine_to_w32 "$1" - func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_nix_to_w32 - - -# func_convert_file_msys_to_cygwin ARG -# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. -# Returns result in func_to_host_file_result. -func_convert_file_msys_to_cygwin () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - func_convert_core_msys_to_w32 "$1" - func_cygpath -u "$func_convert_core_msys_to_w32_result" - func_to_host_file_result="$func_cygpath_result" - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_msys_to_cygwin - - -# func_convert_file_nix_to_cygwin ARG -# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed -# in a wine environment, working winepath, and LT_CYGPATH set. Returns result -# in func_to_host_file_result. -func_convert_file_nix_to_cygwin () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. - func_convert_core_file_wine_to_w32 "$1" - func_cygpath -u "$func_convert_core_file_wine_to_w32_result" - func_to_host_file_result="$func_cygpath_result" - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_nix_to_cygwin - - -############################################# -# $build to $host PATH CONVERSION FUNCTIONS # -############################################# -# invoked via `$to_host_path_cmd ARG' -# -# In each case, ARG is the path to be converted from $build to $host format. -# The result will be available in $func_to_host_path_result. -# -# Path separators are also converted from $build format to $host format. If -# ARG begins or ends with a path separator character, it is preserved (but -# converted to $host format) on output. -# -# All path conversion functions are named using the following convention: -# file name conversion function : func_convert_file_X_to_Y () -# path conversion function : func_convert_path_X_to_Y () -# where, for any given $build/$host combination the 'X_to_Y' value is the -# same. If conversion functions are added for new $build/$host combinations, -# the two new functions must follow this pattern, or func_init_to_host_path_cmd -# will break. - - -# func_init_to_host_path_cmd -# Ensures that function "pointer" variable $to_host_path_cmd is set to the -# appropriate value, based on the value of $to_host_file_cmd. -to_host_path_cmd= -func_init_to_host_path_cmd () -{ - $opt_debug - if test -z "$to_host_path_cmd"; then - func_stripname 'func_convert_file_' '' "$to_host_file_cmd" - to_host_path_cmd="func_convert_path_${func_stripname_result}" - fi -} - - -# func_to_host_path ARG -# Converts the path ARG from $build format to $host format. Return result -# in func_to_host_path_result. -func_to_host_path () -{ - $opt_debug - func_init_to_host_path_cmd - $to_host_path_cmd "$1" -} -# end func_to_host_path - - -# func_convert_path_noop ARG -# Copy ARG to func_to_host_path_result. -func_convert_path_noop () -{ - func_to_host_path_result="$1" -} -# end func_convert_path_noop - - -# func_convert_path_msys_to_w32 ARG -# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic -# conversion to w32 is not available inside the cwrapper. Returns result in -# func_to_host_path_result. -func_convert_path_msys_to_w32 () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # Remove leading and trailing path separator characters from ARG. MSYS - # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; - # and winepath ignores them completely. - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" - func_to_host_path_result="$func_convert_core_msys_to_w32_result" - func_convert_path_check : ";" \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" - fi -} -# end func_convert_path_msys_to_w32 - - -# func_convert_path_cygwin_to_w32 ARG -# Convert path ARG from Cygwin to w32 format. Returns result in -# func_to_host_file_result. -func_convert_path_cygwin_to_w32 () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # See func_convert_path_msys_to_w32: - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` - func_convert_path_check : ";" \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" - fi -} -# end func_convert_path_cygwin_to_w32 - - -# func_convert_path_nix_to_w32 ARG -# Convert path ARG from *nix to w32 format. Requires a wine environment and -# a working winepath. Returns result in func_to_host_file_result. -func_convert_path_nix_to_w32 () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # See func_convert_path_msys_to_w32: - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" - func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" - func_convert_path_check : ";" \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" - fi -} -# end func_convert_path_nix_to_w32 - - -# func_convert_path_msys_to_cygwin ARG -# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. -# Returns result in func_to_host_file_result. -func_convert_path_msys_to_cygwin () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # See func_convert_path_msys_to_w32: - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" - func_cygpath -u -p "$func_convert_core_msys_to_w32_result" - func_to_host_path_result="$func_cygpath_result" - func_convert_path_check : : \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" : "$1" - fi -} -# end func_convert_path_msys_to_cygwin - - -# func_convert_path_nix_to_cygwin ARG -# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a -# a wine environment, working winepath, and LT_CYGPATH set. Returns result in -# func_to_host_file_result. -func_convert_path_nix_to_cygwin () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # Remove leading and trailing path separator characters from - # ARG. msys behavior is inconsistent here, cygpath turns them - # into '.;' and ';.', and winepath ignores them completely. - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" - func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" - func_to_host_path_result="$func_cygpath_result" - func_convert_path_check : : \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" : "$1" - fi -} -# end func_convert_path_nix_to_cygwin - - -# func_mode_compile arg... -func_mode_compile () -{ - $opt_debug - # Get the compilation command and the source file. - base_compile= - srcfile="$nonopt" # always keep a non-empty value in "srcfile" - suppress_opt=yes - suppress_output= - arg_mode=normal - libobj= - later= - pie_flag= - - for arg - do - case $arg_mode in - arg ) - # do not "continue". Instead, add this to base_compile - lastarg="$arg" - arg_mode=normal - ;; - - target ) - libobj="$arg" - arg_mode=normal - continue - ;; - - normal ) - # Accept any command-line options. - case $arg in - -o) - test -n "$libobj" && \ - func_fatal_error "you cannot specify \`-o' more than once" - arg_mode=target - continue - ;; - - -pie | -fpie | -fPIE) - func_append pie_flag " $arg" - continue - ;; - - -shared | -static | -prefer-pic | -prefer-non-pic) - func_append later " $arg" - continue - ;; - - -no-suppress) - suppress_opt=no - continue - ;; - - -Xcompiler) - arg_mode=arg # the next one goes into the "base_compile" arg list - continue # The current "srcfile" will either be retained or - ;; # replaced later. I would guess that would be a bug. - - -Wc,*) - func_stripname '-Wc,' '' "$arg" - args=$func_stripname_result - lastarg= - save_ifs="$IFS"; IFS=',' - for arg in $args; do - IFS="$save_ifs" - func_append_quoted lastarg "$arg" - done - IFS="$save_ifs" - func_stripname ' ' '' "$lastarg" - lastarg=$func_stripname_result - - # Add the arguments to base_compile. - func_append base_compile " $lastarg" - continue - ;; - - *) - # Accept the current argument as the source file. - # The previous "srcfile" becomes the current argument. - # - lastarg="$srcfile" - srcfile="$arg" - ;; - esac # case $arg - ;; - esac # case $arg_mode - - # Aesthetically quote the previous argument. - func_append_quoted base_compile "$lastarg" - done # for arg - - case $arg_mode in - arg) - func_fatal_error "you must specify an argument for -Xcompile" - ;; - target) - func_fatal_error "you must specify a target with \`-o'" - ;; - *) - # Get the name of the library object. - test -z "$libobj" && { - func_basename "$srcfile" - libobj="$func_basename_result" - } - ;; - esac - - # Recognize several different file suffixes. - # If the user specifies -o file.o, it is replaced with file.lo - case $libobj in - *.[cCFSifmso] | \ - *.ada | *.adb | *.ads | *.asm | \ - *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ - *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) - func_xform "$libobj" - libobj=$func_xform_result - ;; - esac - - case $libobj in - *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; - *) - func_fatal_error "cannot determine name of library object from \`$libobj'" - ;; - esac - - func_infer_tag $base_compile - - for arg in $later; do - case $arg in - -shared) - test "$build_libtool_libs" != yes && \ - func_fatal_configuration "can not build a shared library" - build_old_libs=no - continue - ;; - - -static) - build_libtool_libs=no - build_old_libs=yes - continue - ;; - - -prefer-pic) - pic_mode=yes - continue - ;; - - -prefer-non-pic) - pic_mode=no - continue - ;; - esac - done - - func_quote_for_eval "$libobj" - test "X$libobj" != "X$func_quote_for_eval_result" \ - && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ - && func_warning "libobj name \`$libobj' may not contain shell special characters." - func_dirname_and_basename "$obj" "/" "" - objname="$func_basename_result" - xdir="$func_dirname_result" - lobj=${xdir}$objdir/$objname - - test -z "$base_compile" && \ - func_fatal_help "you must specify a compilation command" - - # Delete any leftover library objects. - if test "$build_old_libs" = yes; then - removelist="$obj $lobj $libobj ${libobj}T" - else - removelist="$lobj $libobj ${libobj}T" - fi - - # On Cygwin there's no "real" PIC flag so we must build both object types - case $host_os in - cygwin* | mingw* | pw32* | os2* | cegcc*) - pic_mode=default - ;; - esac - if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then - # non-PIC code in shared libraries is not supported - pic_mode=default - fi - - # Calculate the filename of the output object if compiler does - # not support -o with -c - if test "$compiler_c_o" = no; then - output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} - lockfile="$output_obj.lock" - else - output_obj= - need_locks=no - lockfile= - fi - - # Lock this critical section if it is needed - # We use this script file to make the link, it avoids creating a new file - if test "$need_locks" = yes; then - until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do - func_echo "Waiting for $lockfile to be removed" - sleep 2 - done - elif test "$need_locks" = warn; then - if test -f "$lockfile"; then - $ECHO "\ -*** ERROR, $lockfile exists and contains: -`cat $lockfile 2>/dev/null` - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $opt_dry_run || $RM $removelist - exit $EXIT_FAILURE - fi - func_append removelist " $output_obj" - $ECHO "$srcfile" > "$lockfile" - fi - - $opt_dry_run || $RM $removelist - func_append removelist " $lockfile" - trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 - - func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 - srcfile=$func_to_tool_file_result - func_quote_for_eval "$srcfile" - qsrcfile=$func_quote_for_eval_result - - # Only build a PIC object if we are building libtool libraries. - if test "$build_libtool_libs" = yes; then - # Without this assignment, base_compile gets emptied. - fbsd_hideous_sh_bug=$base_compile - - if test "$pic_mode" != no; then - command="$base_compile $qsrcfile $pic_flag" - else - # Don't build PIC code - command="$base_compile $qsrcfile" - fi - - func_mkdir_p "$xdir$objdir" - - if test -z "$output_obj"; then - # Place PIC objects in $objdir - func_append command " -o $lobj" - fi - - func_show_eval_locale "$command" \ - 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' - - if test "$need_locks" = warn && - test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then - $ECHO "\ -*** ERROR, $lockfile contains: -`cat $lockfile 2>/dev/null` - -but it should contain: -$srcfile - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $opt_dry_run || $RM $removelist - exit $EXIT_FAILURE - fi - - # Just move the object if needed, then go on to compile the next one - if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then - func_show_eval '$MV "$output_obj" "$lobj"' \ - 'error=$?; $opt_dry_run || $RM $removelist; exit $error' - fi - - # Allow error messages only from the first compilation. - if test "$suppress_opt" = yes; then - suppress_output=' >/dev/null 2>&1' - fi - fi - - # Only build a position-dependent object if we build old libraries. - if test "$build_old_libs" = yes; then - if test "$pic_mode" != yes; then - # Don't build PIC code - command="$base_compile $qsrcfile$pie_flag" - else - command="$base_compile $qsrcfile $pic_flag" - fi - if test "$compiler_c_o" = yes; then - func_append command " -o $obj" - fi - - # Suppress compiler output if we already did a PIC compilation. - func_append command "$suppress_output" - func_show_eval_locale "$command" \ - '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' - - if test "$need_locks" = warn && - test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then - $ECHO "\ -*** ERROR, $lockfile contains: -`cat $lockfile 2>/dev/null` - -but it should contain: -$srcfile - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $opt_dry_run || $RM $removelist - exit $EXIT_FAILURE - fi - - # Just move the object if needed - if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then - func_show_eval '$MV "$output_obj" "$obj"' \ - 'error=$?; $opt_dry_run || $RM $removelist; exit $error' - fi - fi - - $opt_dry_run || { - func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" - - # Unlock the critical section if it was locked - if test "$need_locks" != no; then - removelist=$lockfile - $RM "$lockfile" - fi - } - - exit $EXIT_SUCCESS -} - -$opt_help || { - test "$opt_mode" = compile && func_mode_compile ${1+"$@"} -} - -func_mode_help () -{ - # We need to display help for each of the modes. - case $opt_mode in - "") - # Generic help is extracted from the usage comments - # at the start of this file. - func_help - ;; - - clean) - $ECHO \ -"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... - -Remove files from the build directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. - -If FILE is a libtool library, object or program, all the files associated -with it are deleted. Otherwise, only FILE itself is deleted using RM." - ;; - - compile) - $ECHO \ -"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE - -Compile a source file into a libtool library object. - -This mode accepts the following additional options: - - -o OUTPUT-FILE set the output file name to OUTPUT-FILE - -no-suppress do not suppress compiler output for multiple passes - -prefer-pic try to build PIC objects only - -prefer-non-pic try to build non-PIC objects only - -shared do not build a \`.o' file suitable for static linking - -static only build a \`.o' file suitable for static linking - -Wc,FLAG pass FLAG directly to the compiler - -COMPILE-COMMAND is a command to be used in creating a \`standard' object file -from the given SOURCEFILE. - -The output file name is determined by removing the directory component from -SOURCEFILE, then substituting the C source code suffix \`.c' with the -library object suffix, \`.lo'." - ;; - - execute) - $ECHO \ -"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... - -Automatically set library path, then run a program. - -This mode accepts the following additional options: - - -dlopen FILE add the directory containing FILE to the library path - -This mode sets the library path environment variable according to \`-dlopen' -flags. - -If any of the ARGS are libtool executable wrappers, then they are translated -into their corresponding uninstalled binary, and any of their required library -directories are added to the library path. - -Then, COMMAND is executed, with ARGS as arguments." - ;; - - finish) - $ECHO \ -"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... - -Complete the installation of libtool libraries. - -Each LIBDIR is a directory that contains libtool libraries. - -The commands that this mode executes may require superuser privileges. Use -the \`--dry-run' option if you just want to see what would be executed." - ;; - - install) - $ECHO \ -"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... - -Install executables or libraries. - -INSTALL-COMMAND is the installation command. The first component should be -either the \`install' or \`cp' program. - -The following components of INSTALL-COMMAND are treated specially: - - -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation - -The rest of the components are interpreted as arguments to that command (only -BSD-compatible install options are recognized)." - ;; - - link) - $ECHO \ -"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... - -Link object files or libraries together to form another library, or to -create an executable program. - -LINK-COMMAND is a command using the C compiler that you would use to create -a program from several object files. - -The following components of LINK-COMMAND are treated specially: - - -all-static do not do any dynamic linking at all - -avoid-version do not add a version suffix if possible - -bindir BINDIR specify path to binaries directory (for systems where - libraries must be found in the PATH setting at runtime) - -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime - -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols - -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) - -export-symbols SYMFILE - try to export only the symbols listed in SYMFILE - -export-symbols-regex REGEX - try to export only the symbols matching REGEX - -LLIBDIR search LIBDIR for required installed libraries - -lNAME OUTPUT-FILE requires the installed library libNAME - -module build a library that can dlopened - -no-fast-install disable the fast-install mode - -no-install link a not-installable executable - -no-undefined declare that a library does not refer to external symbols - -o OUTPUT-FILE create OUTPUT-FILE from the specified objects - -objectlist FILE Use a list of object files found in FILE to specify objects - -precious-files-regex REGEX - don't remove output files matching REGEX - -release RELEASE specify package release information - -rpath LIBDIR the created library will eventually be installed in LIBDIR - -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries - -shared only do dynamic linking of libtool libraries - -shrext SUFFIX override the standard shared library file extension - -static do not do any dynamic linking of uninstalled libtool libraries - -static-libtool-libs - do not do any dynamic linking of libtool libraries - -version-info CURRENT[:REVISION[:AGE]] - specify library version info [each variable defaults to 0] - -weak LIBNAME declare that the target provides the LIBNAME interface - -Wc,FLAG - -Xcompiler FLAG pass linker-specific FLAG directly to the compiler - -Wl,FLAG - -Xlinker FLAG pass linker-specific FLAG directly to the linker - -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) - -All other options (arguments beginning with \`-') are ignored. - -Every other argument is treated as a filename. Files ending in \`.la' are -treated as uninstalled libtool libraries, other files are standard or library -object files. - -If the OUTPUT-FILE ends in \`.la', then a libtool library is created, -only library objects (\`.lo' files) may be specified, and \`-rpath' is -required, except when creating a convenience library. - -If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created -using \`ar' and \`ranlib', or on Windows using \`lib'. - -If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file -is created, otherwise an executable program is created." - ;; - - uninstall) - $ECHO \ -"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... - -Remove libraries from an installation directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. - -If FILE is a libtool library, all the files associated with it are deleted. -Otherwise, only FILE itself is deleted using RM." - ;; - - *) - func_fatal_help "invalid operation mode \`$opt_mode'" - ;; - esac - - echo - $ECHO "Try \`$progname --help' for more information about other modes." -} - -# Now that we've collected a possible --mode arg, show help if necessary -if $opt_help; then - if test "$opt_help" = :; then - func_mode_help - else - { - func_help noexit - for opt_mode in compile link execute install finish uninstall clean; do - func_mode_help - done - } | sed -n '1p; 2,$s/^Usage:/ or: /p' - { - func_help noexit - for opt_mode in compile link execute install finish uninstall clean; do - echo - func_mode_help - done - } | - sed '1d - /^When reporting/,/^Report/{ - H - d - } - $x - /information about other modes/d - /more detailed .*MODE/d - s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' - fi - exit $? -fi - - -# func_mode_execute arg... -func_mode_execute () -{ - $opt_debug - # The first argument is the command name. - cmd="$nonopt" - test -z "$cmd" && \ - func_fatal_help "you must specify a COMMAND" - - # Handle -dlopen flags immediately. - for file in $opt_dlopen; do - test -f "$file" \ - || func_fatal_help "\`$file' is not a file" - - dir= - case $file in - *.la) - func_resolve_sysroot "$file" - file=$func_resolve_sysroot_result - - # Check to see that this really is a libtool archive. - func_lalib_unsafe_p "$file" \ - || func_fatal_help "\`$lib' is not a valid libtool archive" - - # Read the libtool library. - dlname= - library_names= - func_source "$file" - - # Skip this library if it cannot be dlopened. - if test -z "$dlname"; then - # Warn if it was a shared library. - test -n "$library_names" && \ - func_warning "\`$file' was not linked with \`-export-dynamic'" - continue - fi - - func_dirname "$file" "" "." - dir="$func_dirname_result" - - if test -f "$dir/$objdir/$dlname"; then - func_append dir "/$objdir" - else - if test ! -f "$dir/$dlname"; then - func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" - fi - fi - ;; - - *.lo) - # Just add the directory containing the .lo file. - func_dirname "$file" "" "." - dir="$func_dirname_result" - ;; - - *) - func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" - continue - ;; - esac - - # Get the absolute pathname. - absdir=`cd "$dir" && pwd` - test -n "$absdir" && dir="$absdir" - - # Now add the directory to shlibpath_var. - if eval "test -z \"\$$shlibpath_var\""; then - eval "$shlibpath_var=\"\$dir\"" - else - eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" - fi - done - - # This variable tells wrapper scripts just to set shlibpath_var - # rather than running their programs. - libtool_execute_magic="$magic" - - # Check if any of the arguments is a wrapper script. - args= - for file - do - case $file in - -* | *.la | *.lo ) ;; - *) - # Do a test to see if this is really a libtool program. - if func_ltwrapper_script_p "$file"; then - func_source "$file" - # Transform arg to wrapped name. - file="$progdir/$program" - elif func_ltwrapper_executable_p "$file"; then - func_ltwrapper_scriptname "$file" - func_source "$func_ltwrapper_scriptname_result" - # Transform arg to wrapped name. - file="$progdir/$program" - fi - ;; - esac - # Quote arguments (to preserve shell metacharacters). - func_append_quoted args "$file" - done - - if test "X$opt_dry_run" = Xfalse; then - if test -n "$shlibpath_var"; then - # Export the shlibpath_var. - eval "export $shlibpath_var" - fi - - # Restore saved environment variables - for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES - do - eval "if test \"\${save_$lt_var+set}\" = set; then - $lt_var=\$save_$lt_var; export $lt_var - else - $lt_unset $lt_var - fi" - done - - # Now prepare to actually exec the command. - exec_cmd="\$cmd$args" - else - # Display what would be done. - if test -n "$shlibpath_var"; then - eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" - echo "export $shlibpath_var" - fi - $ECHO "$cmd$args" - exit $EXIT_SUCCESS - fi -} - -test "$opt_mode" = execute && func_mode_execute ${1+"$@"} - - -# func_mode_finish arg... -func_mode_finish () -{ - $opt_debug - libs= - libdirs= - admincmds= - - for opt in "$nonopt" ${1+"$@"} - do - if test -d "$opt"; then - func_append libdirs " $opt" - - elif test -f "$opt"; then - if func_lalib_unsafe_p "$opt"; then - func_append libs " $opt" - else - func_warning "\`$opt' is not a valid libtool archive" - fi - - else - func_fatal_error "invalid argument \`$opt'" - fi - done - - if test -n "$libs"; then - if test -n "$lt_sysroot"; then - sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` - sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" - else - sysroot_cmd= - fi - - # Remove sysroot references - if $opt_dry_run; then - for lib in $libs; do - echo "removing references to $lt_sysroot and \`=' prefixes from $lib" - done - else - tmpdir=`func_mktempdir` - for lib in $libs; do - sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ - > $tmpdir/tmp-la - mv -f $tmpdir/tmp-la $lib - done - ${RM}r "$tmpdir" - fi - fi - - if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then - for libdir in $libdirs; do - if test -n "$finish_cmds"; then - # Do each command in the finish commands. - func_execute_cmds "$finish_cmds" 'admincmds="$admincmds -'"$cmd"'"' - fi - if test -n "$finish_eval"; then - # Do the single finish_eval. - eval cmds=\"$finish_eval\" - $opt_dry_run || eval "$cmds" || func_append admincmds " - $cmds" - fi - done - fi - - # Exit here if they wanted silent mode. - $opt_silent && exit $EXIT_SUCCESS - - if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then - echo "----------------------------------------------------------------------" - echo "Libraries have been installed in:" - for libdir in $libdirs; do - $ECHO " $libdir" - done - echo - echo "If you ever happen to want to link against installed libraries" - echo "in a given directory, LIBDIR, you must either use libtool, and" - echo "specify the full pathname of the library, or use the \`-LLIBDIR'" - echo "flag during linking and do at least one of the following:" - if test -n "$shlibpath_var"; then - echo " - add LIBDIR to the \`$shlibpath_var' environment variable" - echo " during execution" - fi - if test -n "$runpath_var"; then - echo " - add LIBDIR to the \`$runpath_var' environment variable" - echo " during linking" - fi - if test -n "$hardcode_libdir_flag_spec"; then - libdir=LIBDIR - eval flag=\"$hardcode_libdir_flag_spec\" - - $ECHO " - use the \`$flag' linker flag" - fi - if test -n "$admincmds"; then - $ECHO " - have your system administrator run these commands:$admincmds" - fi - if test -f /etc/ld.so.conf; then - echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" - fi - echo - - echo "See any operating system documentation about shared libraries for" - case $host in - solaris2.[6789]|solaris2.1[0-9]) - echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" - echo "pages." - ;; - *) - echo "more information, such as the ld(1) and ld.so(8) manual pages." - ;; - esac - echo "----------------------------------------------------------------------" - fi - exit $EXIT_SUCCESS -} - -test "$opt_mode" = finish && func_mode_finish ${1+"$@"} - - -# func_mode_install arg... -func_mode_install () -{ - $opt_debug - # There may be an optional sh(1) argument at the beginning of - # install_prog (especially on Windows NT). - if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || - # Allow the use of GNU shtool's install command. - case $nonopt in *shtool*) :;; *) false;; esac; then - # Aesthetically quote it. - func_quote_for_eval "$nonopt" - install_prog="$func_quote_for_eval_result " - arg=$1 - shift - else - install_prog= - arg=$nonopt - fi - - # The real first argument should be the name of the installation program. - # Aesthetically quote it. - func_quote_for_eval "$arg" - func_append install_prog "$func_quote_for_eval_result" - install_shared_prog=$install_prog - case " $install_prog " in - *[\\\ /]cp\ *) install_cp=: ;; - *) install_cp=false ;; - esac - - # We need to accept at least all the BSD install flags. - dest= - files= - opts= - prev= - install_type= - isdir=no - stripme= - no_mode=: - for arg - do - arg2= - if test -n "$dest"; then - func_append files " $dest" - dest=$arg - continue - fi - - case $arg in - -d) isdir=yes ;; - -f) - if $install_cp; then :; else - prev=$arg - fi - ;; - -g | -m | -o) - prev=$arg - ;; - -s) - stripme=" -s" - continue - ;; - -*) - ;; - *) - # If the previous option needed an argument, then skip it. - if test -n "$prev"; then - if test "x$prev" = x-m && test -n "$install_override_mode"; then - arg2=$install_override_mode - no_mode=false - fi - prev= - else - dest=$arg - continue - fi - ;; - esac - - # Aesthetically quote the argument. - func_quote_for_eval "$arg" - func_append install_prog " $func_quote_for_eval_result" - if test -n "$arg2"; then - func_quote_for_eval "$arg2" - fi - func_append install_shared_prog " $func_quote_for_eval_result" - done - - test -z "$install_prog" && \ - func_fatal_help "you must specify an install program" - - test -n "$prev" && \ - func_fatal_help "the \`$prev' option requires an argument" - - if test -n "$install_override_mode" && $no_mode; then - if $install_cp; then :; else - func_quote_for_eval "$install_override_mode" - func_append install_shared_prog " -m $func_quote_for_eval_result" - fi - fi - - if test -z "$files"; then - if test -z "$dest"; then - func_fatal_help "no file or destination specified" - else - func_fatal_help "you must specify a destination" - fi - fi - - # Strip any trailing slash from the destination. - func_stripname '' '/' "$dest" - dest=$func_stripname_result - - # Check to see that the destination is a directory. - test -d "$dest" && isdir=yes - if test "$isdir" = yes; then - destdir="$dest" - destname= - else - func_dirname_and_basename "$dest" "" "." - destdir="$func_dirname_result" - destname="$func_basename_result" - - # Not a directory, so check to see that there is only one file specified. - set dummy $files; shift - test "$#" -gt 1 && \ - func_fatal_help "\`$dest' is not a directory" - fi - case $destdir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - for file in $files; do - case $file in - *.lo) ;; - *) - func_fatal_help "\`$destdir' must be an absolute directory name" - ;; - esac - done - ;; - esac - - # This variable tells wrapper scripts just to set variables rather - # than running their programs. - libtool_install_magic="$magic" - - staticlibs= - future_libdirs= - current_libdirs= - for file in $files; do - - # Do each installation. - case $file in - *.$libext) - # Do the static libraries later. - func_append staticlibs " $file" - ;; - - *.la) - func_resolve_sysroot "$file" - file=$func_resolve_sysroot_result - - # Check to see that this really is a libtool archive. - func_lalib_unsafe_p "$file" \ - || func_fatal_help "\`$file' is not a valid libtool archive" - - library_names= - old_library= - relink_command= - func_source "$file" - - # Add the libdir to current_libdirs if it is the destination. - if test "X$destdir" = "X$libdir"; then - case "$current_libdirs " in - *" $libdir "*) ;; - *) func_append current_libdirs " $libdir" ;; - esac - else - # Note the libdir as a future libdir. - case "$future_libdirs " in - *" $libdir "*) ;; - *) func_append future_libdirs " $libdir" ;; - esac - fi - - func_dirname "$file" "/" "" - dir="$func_dirname_result" - func_append dir "$objdir" - - if test -n "$relink_command"; then - # Determine the prefix the user has applied to our future dir. - inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` - - # Don't allow the user to place us outside of our expected - # location b/c this prevents finding dependent libraries that - # are installed to the same prefix. - # At present, this check doesn't affect windows .dll's that - # are installed into $libdir/../bin (currently, that works fine) - # but it's something to keep an eye on. - test "$inst_prefix_dir" = "$destdir" && \ - func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" - - if test -n "$inst_prefix_dir"; then - # Stick the inst_prefix_dir data into the link command. - relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` - else - relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` - fi - - func_warning "relinking \`$file'" - func_show_eval "$relink_command" \ - 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' - fi - - # See the names of the shared library. - set dummy $library_names; shift - if test -n "$1"; then - realname="$1" - shift - - srcname="$realname" - test -n "$relink_command" && srcname="$realname"T - - # Install the shared library and build the symlinks. - func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ - 'exit $?' - tstripme="$stripme" - case $host_os in - cygwin* | mingw* | pw32* | cegcc*) - case $realname in - *.dll.a) - tstripme="" - ;; - esac - ;; - esac - if test -n "$tstripme" && test -n "$striplib"; then - func_show_eval "$striplib $destdir/$realname" 'exit $?' - fi - - if test "$#" -gt 0; then - # Delete the old symlinks, and create new ones. - # Try `ln -sf' first, because the `ln' binary might depend on - # the symlink we replace! Solaris /bin/ln does not understand -f, - # so we also need to try rm && ln -s. - for linkname - do - test "$linkname" != "$realname" \ - && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" - done - fi - - # Do each command in the postinstall commands. - lib="$destdir/$realname" - func_execute_cmds "$postinstall_cmds" 'exit $?' - fi - - # Install the pseudo-library for information purposes. - func_basename "$file" - name="$func_basename_result" - instname="$dir/$name"i - func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' - - # Maybe install the static library, too. - test -n "$old_library" && func_append staticlibs " $dir/$old_library" - ;; - - *.lo) - # Install (i.e. copy) a libtool object. - - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" - else - func_basename "$file" - destfile="$func_basename_result" - destfile="$destdir/$destfile" - fi - - # Deduce the name of the destination old-style object file. - case $destfile in - *.lo) - func_lo2o "$destfile" - staticdest=$func_lo2o_result - ;; - *.$objext) - staticdest="$destfile" - destfile= - ;; - *) - func_fatal_help "cannot copy a libtool object to \`$destfile'" - ;; - esac - - # Install the libtool object if requested. - test -n "$destfile" && \ - func_show_eval "$install_prog $file $destfile" 'exit $?' - - # Install the old object if enabled. - if test "$build_old_libs" = yes; then - # Deduce the name of the old-style object file. - func_lo2o "$file" - staticobj=$func_lo2o_result - func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' - fi - exit $EXIT_SUCCESS - ;; - - *) - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" - else - func_basename "$file" - destfile="$func_basename_result" - destfile="$destdir/$destfile" - fi - - # If the file is missing, and there is a .exe on the end, strip it - # because it is most likely a libtool script we actually want to - # install - stripped_ext="" - case $file in - *.exe) - if test ! -f "$file"; then - func_stripname '' '.exe' "$file" - file=$func_stripname_result - stripped_ext=".exe" - fi - ;; - esac - - # Do a test to see if this is really a libtool program. - case $host in - *cygwin* | *mingw*) - if func_ltwrapper_executable_p "$file"; then - func_ltwrapper_scriptname "$file" - wrapper=$func_ltwrapper_scriptname_result - else - func_stripname '' '.exe' "$file" - wrapper=$func_stripname_result - fi - ;; - *) - wrapper=$file - ;; - esac - if func_ltwrapper_script_p "$wrapper"; then - notinst_deplibs= - relink_command= - - func_source "$wrapper" - - # Check the variables that should have been set. - test -z "$generated_by_libtool_version" && \ - func_fatal_error "invalid libtool wrapper script \`$wrapper'" - - finalize=yes - for lib in $notinst_deplibs; do - # Check to see that each library is installed. - libdir= - if test -f "$lib"; then - func_source "$lib" - fi - libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test - if test -n "$libdir" && test ! -f "$libfile"; then - func_warning "\`$lib' has not been installed in \`$libdir'" - finalize=no - fi - done - - relink_command= - func_source "$wrapper" - - outputname= - if test "$fast_install" = no && test -n "$relink_command"; then - $opt_dry_run || { - if test "$finalize" = yes; then - tmpdir=`func_mktempdir` - func_basename "$file$stripped_ext" - file="$func_basename_result" - outputname="$tmpdir/$file" - # Replace the output file specification. - relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` - - $opt_silent || { - func_quote_for_expand "$relink_command" - eval "func_echo $func_quote_for_expand_result" - } - if eval "$relink_command"; then : - else - func_error "error: relink \`$file' with the above command before installing it" - $opt_dry_run || ${RM}r "$tmpdir" - continue - fi - file="$outputname" - else - func_warning "cannot relink \`$file'" - fi - } - else - # Install the binary that we compiled earlier. - file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` - fi - fi - - # remove .exe since cygwin /usr/bin/install will append another - # one anyway - case $install_prog,$host in - */usr/bin/install*,*cygwin*) - case $file:$destfile in - *.exe:*.exe) - # this is ok - ;; - *.exe:*) - destfile=$destfile.exe - ;; - *:*.exe) - func_stripname '' '.exe' "$destfile" - destfile=$func_stripname_result - ;; - esac - ;; - esac - func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' - $opt_dry_run || if test -n "$outputname"; then - ${RM}r "$tmpdir" - fi - ;; - esac - done - - for file in $staticlibs; do - func_basename "$file" - name="$func_basename_result" - - # Set up the ranlib parameters. - oldlib="$destdir/$name" - func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 - tool_oldlib=$func_to_tool_file_result - - func_show_eval "$install_prog \$file \$oldlib" 'exit $?' - - if test -n "$stripme" && test -n "$old_striplib"; then - func_show_eval "$old_striplib $tool_oldlib" 'exit $?' - fi - - # Do each command in the postinstall commands. - func_execute_cmds "$old_postinstall_cmds" 'exit $?' - done - - test -n "$future_libdirs" && \ - func_warning "remember to run \`$progname --finish$future_libdirs'" - - if test -n "$current_libdirs"; then - # Maybe just do a dry run. - $opt_dry_run && current_libdirs=" -n$current_libdirs" - exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' - else - exit $EXIT_SUCCESS - fi -} - -test "$opt_mode" = install && func_mode_install ${1+"$@"} - - -# func_generate_dlsyms outputname originator pic_p -# Extract symbols from dlprefiles and create ${outputname}S.o with -# a dlpreopen symbol table. -func_generate_dlsyms () -{ - $opt_debug - my_outputname="$1" - my_originator="$2" - my_pic_p="${3-no}" - my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` - my_dlsyms= - - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - if test -n "$NM" && test -n "$global_symbol_pipe"; then - my_dlsyms="${my_outputname}S.c" - else - func_error "not configured to extract global symbols from dlpreopened files" - fi - fi - - if test -n "$my_dlsyms"; then - case $my_dlsyms in - "") ;; - *.c) - # Discover the nlist of each of the dlfiles. - nlist="$output_objdir/${my_outputname}.nm" - - func_show_eval "$RM $nlist ${nlist}S ${nlist}T" - - # Parse the name list into a source file. - func_verbose "creating $output_objdir/$my_dlsyms" - - $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ -/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ -/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ - -#ifdef __cplusplus -extern \"C\" { -#endif - -#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) -#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" -#endif - -/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) -/* DATA imports from DLLs on WIN32 con't be const, because runtime - relocations are performed -- see ld's documentation on pseudo-relocs. */ -# define LT_DLSYM_CONST -#elif defined(__osf__) -/* This system does not cope well with relocations in const data. */ -# define LT_DLSYM_CONST -#else -# define LT_DLSYM_CONST const -#endif - -/* External symbol declarations for the compiler. */\ -" - - if test "$dlself" = yes; then - func_verbose "generating symbol list for \`$output'" - - $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" - - # Add our own program objects to the symbol list. - progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` - for progfile in $progfiles; do - func_to_tool_file "$progfile" func_convert_file_msys_to_w32 - func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" - $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" - done - - if test -n "$exclude_expsyms"; then - $opt_dry_run || { - eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' - eval '$MV "$nlist"T "$nlist"' - } - fi - - if test -n "$export_symbols_regex"; then - $opt_dry_run || { - eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' - eval '$MV "$nlist"T "$nlist"' - } - fi - - # Prepare the list of exported symbols - if test -z "$export_symbols"; then - export_symbols="$output_objdir/$outputname.exp" - $opt_dry_run || { - $RM $export_symbols - eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' - case $host in - *cygwin* | *mingw* | *cegcc* ) - eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' - ;; - esac - } - else - $opt_dry_run || { - eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' - eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' - eval '$MV "$nlist"T "$nlist"' - case $host in - *cygwin* | *mingw* | *cegcc* ) - eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' - ;; - esac - } - fi - fi - - for dlprefile in $dlprefiles; do - func_verbose "extracting global C symbols from \`$dlprefile'" - func_basename "$dlprefile" - name="$func_basename_result" - case $host in - *cygwin* | *mingw* | *cegcc* ) - # if an import library, we need to obtain dlname - if func_win32_import_lib_p "$dlprefile"; then - func_tr_sh "$dlprefile" - eval "curr_lafile=\$libfile_$func_tr_sh_result" - dlprefile_dlbasename="" - if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then - # Use subshell, to avoid clobbering current variable values - dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` - if test -n "$dlprefile_dlname" ; then - func_basename "$dlprefile_dlname" - dlprefile_dlbasename="$func_basename_result" - else - # no lafile. user explicitly requested -dlpreopen . - $sharedlib_from_linklib_cmd "$dlprefile" - dlprefile_dlbasename=$sharedlib_from_linklib_result - fi - fi - $opt_dry_run || { - if test -n "$dlprefile_dlbasename" ; then - eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' - else - func_warning "Could not compute DLL name from $name" - eval '$ECHO ": $name " >> "$nlist"' - fi - func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 - eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | - $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" - } - else # not an import lib - $opt_dry_run || { - eval '$ECHO ": $name " >> "$nlist"' - func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 - eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" - } - fi - ;; - *) - $opt_dry_run || { - eval '$ECHO ": $name " >> "$nlist"' - func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 - eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" - } - ;; - esac - done - - $opt_dry_run || { - # Make sure we have at least an empty file. - test -f "$nlist" || : > "$nlist" - - if test -n "$exclude_expsyms"; then - $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T - $MV "$nlist"T "$nlist" - fi - - # Try sorting and uniquifying the output. - if $GREP -v "^: " < "$nlist" | - if sort -k 3 /dev/null 2>&1; then - sort -k 3 - else - sort +2 - fi | - uniq > "$nlist"S; then - : - else - $GREP -v "^: " < "$nlist" > "$nlist"S - fi - - if test -f "$nlist"S; then - eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' - else - echo '/* NONE */' >> "$output_objdir/$my_dlsyms" - fi - - echo >> "$output_objdir/$my_dlsyms" "\ - -/* The mapping between symbol names and symbols. */ -typedef struct { - const char *name; - void *address; -} lt_dlsymlist; -extern LT_DLSYM_CONST lt_dlsymlist -lt_${my_prefix}_LTX_preloaded_symbols[]; -LT_DLSYM_CONST lt_dlsymlist -lt_${my_prefix}_LTX_preloaded_symbols[] = -{\ - { \"$my_originator\", (void *) 0 }," - - case $need_lib_prefix in - no) - eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" - ;; - *) - eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" - ;; - esac - echo >> "$output_objdir/$my_dlsyms" "\ - {0, (void *) 0} -}; - -/* This works around a problem in FreeBSD linker */ -#ifdef FREEBSD_WORKAROUND -static const void *lt_preloaded_setup() { - return lt_${my_prefix}_LTX_preloaded_symbols; -} -#endif - -#ifdef __cplusplus -} -#endif\ -" - } # !$opt_dry_run - - pic_flag_for_symtable= - case "$compile_command " in - *" -static "*) ;; - *) - case $host in - # compiling the symbol table file with pic_flag works around - # a FreeBSD bug that causes programs to crash when -lm is - # linked before any other PIC object. But we must not use - # pic_flag when linking with -static. The problem exists in - # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. - *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) - pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; - *-*-hpux*) - pic_flag_for_symtable=" $pic_flag" ;; - *) - if test "X$my_pic_p" != Xno; then - pic_flag_for_symtable=" $pic_flag" - fi - ;; - esac - ;; - esac - symtab_cflags= - for arg in $LTCFLAGS; do - case $arg in - -pie | -fpie | -fPIE) ;; - *) func_append symtab_cflags " $arg" ;; - esac - done - - # Now compile the dynamic symbol file. - func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' - - # Clean up the generated files. - func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' - - # Transform the symbol file into the correct name. - symfileobj="$output_objdir/${my_outputname}S.$objext" - case $host in - *cygwin* | *mingw* | *cegcc* ) - if test -f "$output_objdir/$my_outputname.def"; then - compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` - finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` - else - compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` - finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` - fi - ;; - *) - compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` - finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` - ;; - esac - ;; - *) - func_fatal_error "unknown suffix for \`$my_dlsyms'" - ;; - esac - else - # We keep going just in case the user didn't refer to - # lt_preloaded_symbols. The linker will fail if global_symbol_pipe - # really was required. - - # Nullify the symbol file. - compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` - finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` - fi -} - -# func_win32_libid arg -# return the library type of file 'arg' -# -# Need a lot of goo to handle *both* DLLs and import libs -# Has to be a shell function in order to 'eat' the argument -# that is supplied when $file_magic_command is called. -# Despite the name, also deal with 64 bit binaries. -func_win32_libid () -{ - $opt_debug - win32_libid_type="unknown" - win32_fileres=`file -L $1 2>/dev/null` - case $win32_fileres in - *ar\ archive\ import\ library*) # definitely import - win32_libid_type="x86 archive import" - ;; - *ar\ archive*) # could be an import, or static - # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. - if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | - $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then - func_to_tool_file "$1" func_convert_file_msys_to_w32 - win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | - $SED -n -e ' - 1,100{ - / I /{ - s,.*,import, - p - q - } - }'` - case $win32_nmres in - import*) win32_libid_type="x86 archive import";; - *) win32_libid_type="x86 archive static";; - esac - fi - ;; - *DLL*) - win32_libid_type="x86 DLL" - ;; - *executable*) # but shell scripts are "executable" too... - case $win32_fileres in - *MS\ Windows\ PE\ Intel*) - win32_libid_type="x86 DLL" - ;; - esac - ;; - esac - $ECHO "$win32_libid_type" -} - -# func_cygming_dll_for_implib ARG -# -# Platform-specific function to extract the -# name of the DLL associated with the specified -# import library ARG. -# Invoked by eval'ing the libtool variable -# $sharedlib_from_linklib_cmd -# Result is available in the variable -# $sharedlib_from_linklib_result -func_cygming_dll_for_implib () -{ - $opt_debug - sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` -} - -# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs -# -# The is the core of a fallback implementation of a -# platform-specific function to extract the name of the -# DLL associated with the specified import library LIBNAME. -# -# SECTION_NAME is either .idata$6 or .idata$7, depending -# on the platform and compiler that created the implib. -# -# Echos the name of the DLL associated with the -# specified import library. -func_cygming_dll_for_implib_fallback_core () -{ - $opt_debug - match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` - $OBJDUMP -s --section "$1" "$2" 2>/dev/null | - $SED '/^Contents of section '"$match_literal"':/{ - # Place marker at beginning of archive member dllname section - s/.*/====MARK====/ - p - d - } - # These lines can sometimes be longer than 43 characters, but - # are always uninteresting - /:[ ]*file format pe[i]\{,1\}-/d - /^In archive [^:]*:/d - # Ensure marker is printed - /^====MARK====/p - # Remove all lines with less than 43 characters - /^.\{43\}/!d - # From remaining lines, remove first 43 characters - s/^.\{43\}//' | - $SED -n ' - # Join marker and all lines until next marker into a single line - /^====MARK====/ b para - H - $ b para - b - :para - x - s/\n//g - # Remove the marker - s/^====MARK====// - # Remove trailing dots and whitespace - s/[\. \t]*$// - # Print - /./p' | - # we now have a list, one entry per line, of the stringified - # contents of the appropriate section of all members of the - # archive which possess that section. Heuristic: eliminate - # all those which have a first or second character that is - # a '.' (that is, objdump's representation of an unprintable - # character.) This should work for all archives with less than - # 0x302f exports -- but will fail for DLLs whose name actually - # begins with a literal '.' or a single character followed by - # a '.'. - # - # Of those that remain, print the first one. - $SED -e '/^\./d;/^.\./d;q' -} - -# func_cygming_gnu_implib_p ARG -# This predicate returns with zero status (TRUE) if -# ARG is a GNU/binutils-style import library. Returns -# with nonzero status (FALSE) otherwise. -func_cygming_gnu_implib_p () -{ - $opt_debug - func_to_tool_file "$1" func_convert_file_msys_to_w32 - func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` - test -n "$func_cygming_gnu_implib_tmp" -} - -# func_cygming_ms_implib_p ARG -# This predicate returns with zero status (TRUE) if -# ARG is an MS-style import library. Returns -# with nonzero status (FALSE) otherwise. -func_cygming_ms_implib_p () -{ - $opt_debug - func_to_tool_file "$1" func_convert_file_msys_to_w32 - func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` - test -n "$func_cygming_ms_implib_tmp" -} - -# func_cygming_dll_for_implib_fallback ARG -# Platform-specific function to extract the -# name of the DLL associated with the specified -# import library ARG. -# -# This fallback implementation is for use when $DLLTOOL -# does not support the --identify-strict option. -# Invoked by eval'ing the libtool variable -# $sharedlib_from_linklib_cmd -# Result is available in the variable -# $sharedlib_from_linklib_result -func_cygming_dll_for_implib_fallback () -{ - $opt_debug - if func_cygming_gnu_implib_p "$1" ; then - # binutils import library - sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` - elif func_cygming_ms_implib_p "$1" ; then - # ms-generated import library - sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` - else - # unknown - sharedlib_from_linklib_result="" - fi -} - - -# func_extract_an_archive dir oldlib -func_extract_an_archive () -{ - $opt_debug - f_ex_an_ar_dir="$1"; shift - f_ex_an_ar_oldlib="$1" - if test "$lock_old_archive_extraction" = yes; then - lockfile=$f_ex_an_ar_oldlib.lock - until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do - func_echo "Waiting for $lockfile to be removed" - sleep 2 - done - fi - func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ - 'stat=$?; rm -f "$lockfile"; exit $stat' - if test "$lock_old_archive_extraction" = yes; then - $opt_dry_run || rm -f "$lockfile" - fi - if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then - : - else - func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" - fi -} - - -# func_extract_archives gentop oldlib ... -func_extract_archives () -{ - $opt_debug - my_gentop="$1"; shift - my_oldlibs=${1+"$@"} - my_oldobjs="" - my_xlib="" - my_xabs="" - my_xdir="" - - for my_xlib in $my_oldlibs; do - # Extract the objects. - case $my_xlib in - [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; - *) my_xabs=`pwd`"/$my_xlib" ;; - esac - func_basename "$my_xlib" - my_xlib="$func_basename_result" - my_xlib_u=$my_xlib - while :; do - case " $extracted_archives " in - *" $my_xlib_u "*) - func_arith $extracted_serial + 1 - extracted_serial=$func_arith_result - my_xlib_u=lt$extracted_serial-$my_xlib ;; - *) break ;; - esac - done - extracted_archives="$extracted_archives $my_xlib_u" - my_xdir="$my_gentop/$my_xlib_u" - - func_mkdir_p "$my_xdir" - - case $host in - *-darwin*) - func_verbose "Extracting $my_xabs" - # Do not bother doing anything if just a dry run - $opt_dry_run || { - darwin_orig_dir=`pwd` - cd $my_xdir || exit $? - darwin_archive=$my_xabs - darwin_curdir=`pwd` - darwin_base_archive=`basename "$darwin_archive"` - darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` - if test -n "$darwin_arches"; then - darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` - darwin_arch= - func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" - for darwin_arch in $darwin_arches ; do - func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" - $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" - cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" - func_extract_an_archive "`pwd`" "${darwin_base_archive}" - cd "$darwin_curdir" - $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" - done # $darwin_arches - ## Okay now we've a bunch of thin objects, gotta fatten them up :) - darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` - darwin_file= - darwin_files= - for darwin_file in $darwin_filelist; do - darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` - $LIPO -create -output "$darwin_file" $darwin_files - done # $darwin_filelist - $RM -rf unfat-$$ - cd "$darwin_orig_dir" - else - cd $darwin_orig_dir - func_extract_an_archive "$my_xdir" "$my_xabs" - fi # $darwin_arches - } # !$opt_dry_run - ;; - *) - func_extract_an_archive "$my_xdir" "$my_xabs" - ;; - esac - my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` - done - - func_extract_archives_result="$my_oldobjs" -} - - -# func_emit_wrapper [arg=no] -# -# Emit a libtool wrapper script on stdout. -# Don't directly open a file because we may want to -# incorporate the script contents within a cygwin/mingw -# wrapper executable. Must ONLY be called from within -# func_mode_link because it depends on a number of variables -# set therein. -# -# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR -# variable will take. If 'yes', then the emitted script -# will assume that the directory in which it is stored is -# the $objdir directory. This is a cygwin/mingw-specific -# behavior. -func_emit_wrapper () -{ - func_emit_wrapper_arg1=${1-no} - - $ECHO "\ -#! $SHELL - -# $output - temporary wrapper script for $objdir/$outputname -# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION -# -# The $output program cannot be directly executed until all the libtool -# libraries that it depends on are installed. -# -# This wrapper script should never be moved out of the build directory. -# If it is, it will not operate correctly. - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -sed_quote_subst='$sed_quote_subst' - -# Be Bourne compatible -if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac -fi -BIN_SH=xpg4; export BIN_SH # for Tru64 -DUALCASE=1; export DUALCASE # for MKS sh - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -relink_command=\"$relink_command\" - -# This environment variable determines our operation mode. -if test \"\$libtool_install_magic\" = \"$magic\"; then - # install mode needs the following variables: - generated_by_libtool_version='$macro_version' - notinst_deplibs='$notinst_deplibs' -else - # When we are sourced in execute mode, \$file and \$ECHO are already set. - if test \"\$libtool_execute_magic\" != \"$magic\"; then - file=\"\$0\"" - - qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` - $ECHO "\ - -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -\$1 -_LTECHO_EOF' -} - ECHO=\"$qECHO\" - fi - -# Very basic option parsing. These options are (a) specific to -# the libtool wrapper, (b) are identical between the wrapper -# /script/ and the wrapper /executable/ which is used only on -# windows platforms, and (c) all begin with the string "--lt-" -# (application programs are unlikely to have options which match -# this pattern). -# -# There are only two supported options: --lt-debug and -# --lt-dump-script. There is, deliberately, no --lt-help. -# -# The first argument to this parsing function should be the -# script's $0 value, followed by "$@". -lt_option_debug= -func_parse_lt_options () -{ - lt_script_arg0=\$0 - shift - for lt_opt - do - case \"\$lt_opt\" in - --lt-debug) lt_option_debug=1 ;; - --lt-dump-script) - lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` - test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. - lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` - cat \"\$lt_dump_D/\$lt_dump_F\" - exit 0 - ;; - --lt-*) - \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 - exit 1 - ;; - esac - done - - # Print the debug banner immediately: - if test -n \"\$lt_option_debug\"; then - echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 - fi -} - -# Used when --lt-debug. Prints its arguments to stdout -# (redirection is the responsibility of the caller) -func_lt_dump_args () -{ - lt_dump_args_N=1; - for lt_arg - do - \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" - lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` - done -} - -# Core function for launching the target application -func_exec_program_core () -{ -" - case $host in - # Backslashes separate directories on plain windows - *-*-mingw | *-*-os2* | *-cegcc*) - $ECHO "\ - if test -n \"\$lt_option_debug\"; then - \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 - func_lt_dump_args \${1+\"\$@\"} 1>&2 - fi - exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} -" - ;; - - *) - $ECHO "\ - if test -n \"\$lt_option_debug\"; then - \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 - func_lt_dump_args \${1+\"\$@\"} 1>&2 - fi - exec \"\$progdir/\$program\" \${1+\"\$@\"} -" - ;; - esac - $ECHO "\ - \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 - exit 1 -} - -# A function to encapsulate launching the target application -# Strips options in the --lt-* namespace from \$@ and -# launches target application with the remaining arguments. -func_exec_program () -{ - case \" \$* \" in - *\\ --lt-*) - for lt_wr_arg - do - case \$lt_wr_arg in - --lt-*) ;; - *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; - esac - shift - done ;; - esac - func_exec_program_core \${1+\"\$@\"} -} - - # Parse options - func_parse_lt_options \"\$0\" \${1+\"\$@\"} - - # Find the directory that this script lives in. - thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` - test \"x\$thisdir\" = \"x\$file\" && thisdir=. - - # Follow symbolic links until we get to the real thisdir. - file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` - while test -n \"\$file\"; do - destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` - - # If there was a directory component, then change thisdir. - if test \"x\$destdir\" != \"x\$file\"; then - case \"\$destdir\" in - [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; - *) thisdir=\"\$thisdir/\$destdir\" ;; - esac - fi - - file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` - file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` - done - - # Usually 'no', except on cygwin/mingw when embedded into - # the cwrapper. - WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 - if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then - # special case for '.' - if test \"\$thisdir\" = \".\"; then - thisdir=\`pwd\` - fi - # remove .libs from thisdir - case \"\$thisdir\" in - *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; - $objdir ) thisdir=. ;; - esac - fi - - # Try to get the absolute directory name. - absdir=\`cd \"\$thisdir\" && pwd\` - test -n \"\$absdir\" && thisdir=\"\$absdir\" -" - - if test "$fast_install" = yes; then - $ECHO "\ - program=lt-'$outputname'$exeext - progdir=\"\$thisdir/$objdir\" - - if test ! -f \"\$progdir/\$program\" || - { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ - test \"X\$file\" != \"X\$progdir/\$program\"; }; then - - file=\"\$\$-\$program\" - - if test ! -d \"\$progdir\"; then - $MKDIR \"\$progdir\" - else - $RM \"\$progdir/\$file\" - fi" - - $ECHO "\ - - # relink executable if necessary - if test -n \"\$relink_command\"; then - if relink_command_output=\`eval \$relink_command 2>&1\`; then : - else - $ECHO \"\$relink_command_output\" >&2 - $RM \"\$progdir/\$file\" - exit 1 - fi - fi - - $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || - { $RM \"\$progdir/\$program\"; - $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } - $RM \"\$progdir/\$file\" - fi" - else - $ECHO "\ - program='$outputname' - progdir=\"\$thisdir/$objdir\" -" - fi - - $ECHO "\ - - if test -f \"\$progdir/\$program\"; then" - - # fixup the dll searchpath if we need to. - # - # Fix the DLL searchpath if we need to. Do this before prepending - # to shlibpath, because on Windows, both are PATH and uninstalled - # libraries must come first. - if test -n "$dllsearchpath"; then - $ECHO "\ - # Add the dll search path components to the executable PATH - PATH=$dllsearchpath:\$PATH -" - fi - - # Export our shlibpath_var if we have one. - if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then - $ECHO "\ - # Add our own library path to $shlibpath_var - $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" - - # Some systems cannot cope with colon-terminated $shlibpath_var - # The second colon is a workaround for a bug in BeOS R4 sed - $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` - - export $shlibpath_var -" - fi - - $ECHO "\ - if test \"\$libtool_execute_magic\" != \"$magic\"; then - # Run the actual program with our arguments. - func_exec_program \${1+\"\$@\"} - fi - else - # The program doesn't exist. - \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 - \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 - \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 - exit 1 - fi -fi\ -" -} - - -# func_emit_cwrapperexe_src -# emit the source code for a wrapper executable on stdout -# Must ONLY be called from within func_mode_link because -# it depends on a number of variable set therein. -func_emit_cwrapperexe_src () -{ - cat < -#include -#ifdef _MSC_VER -# include -# include -# include -#else -# include -# include -# ifdef __CYGWIN__ -# include -# endif -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -/* declarations of non-ANSI functions */ -#if defined(__MINGW32__) -# ifdef __STRICT_ANSI__ -int _putenv (const char *); -# endif -#elif defined(__CYGWIN__) -# ifdef __STRICT_ANSI__ -char *realpath (const char *, char *); -int putenv (char *); -int setenv (const char *, const char *, int); -# endif -/* #elif defined (other platforms) ... */ -#endif - -/* portability defines, excluding path handling macros */ -#if defined(_MSC_VER) -# define setmode _setmode -# define stat _stat -# define chmod _chmod -# define getcwd _getcwd -# define putenv _putenv -# define S_IXUSR _S_IEXEC -# ifndef _INTPTR_T_DEFINED -# define _INTPTR_T_DEFINED -# define intptr_t int -# endif -#elif defined(__MINGW32__) -# define setmode _setmode -# define stat _stat -# define chmod _chmod -# define getcwd _getcwd -# define putenv _putenv -#elif defined(__CYGWIN__) -# define HAVE_SETENV -# define FOPEN_WB "wb" -/* #elif defined (other platforms) ... */ -#endif - -#if defined(PATH_MAX) -# define LT_PATHMAX PATH_MAX -#elif defined(MAXPATHLEN) -# define LT_PATHMAX MAXPATHLEN -#else -# define LT_PATHMAX 1024 -#endif - -#ifndef S_IXOTH -# define S_IXOTH 0 -#endif -#ifndef S_IXGRP -# define S_IXGRP 0 -#endif - -/* path handling portability macros */ -#ifndef DIR_SEPARATOR -# define DIR_SEPARATOR '/' -# define PATH_SEPARATOR ':' -#endif - -#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ - defined (__OS2__) -# define HAVE_DOS_BASED_FILE_SYSTEM -# define FOPEN_WB "wb" -# ifndef DIR_SEPARATOR_2 -# define DIR_SEPARATOR_2 '\\' -# endif -# ifndef PATH_SEPARATOR_2 -# define PATH_SEPARATOR_2 ';' -# endif -#endif - -#ifndef DIR_SEPARATOR_2 -# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) -#else /* DIR_SEPARATOR_2 */ -# define IS_DIR_SEPARATOR(ch) \ - (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) -#endif /* DIR_SEPARATOR_2 */ - -#ifndef PATH_SEPARATOR_2 -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) -#else /* PATH_SEPARATOR_2 */ -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) -#endif /* PATH_SEPARATOR_2 */ - -#ifndef FOPEN_WB -# define FOPEN_WB "w" -#endif -#ifndef _O_BINARY -# define _O_BINARY 0 -#endif - -#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) -#define XFREE(stale) do { \ - if (stale) { free ((void *) stale); stale = 0; } \ -} while (0) - -#if defined(LT_DEBUGWRAPPER) -static int lt_debug = 1; -#else -static int lt_debug = 0; -#endif - -const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ - -void *xmalloc (size_t num); -char *xstrdup (const char *string); -const char *base_name (const char *name); -char *find_executable (const char *wrapper); -char *chase_symlinks (const char *pathspec); -int make_executable (const char *path); -int check_executable (const char *path); -char *strendzap (char *str, const char *pat); -void lt_debugprintf (const char *file, int line, const char *fmt, ...); -void lt_fatal (const char *file, int line, const char *message, ...); -static const char *nonnull (const char *s); -static const char *nonempty (const char *s); -void lt_setenv (const char *name, const char *value); -char *lt_extend_str (const char *orig_value, const char *add, int to_end); -void lt_update_exe_path (const char *name, const char *value); -void lt_update_lib_path (const char *name, const char *value); -char **prepare_spawn (char **argv); -void lt_dump_script (FILE *f); -EOF - - cat <= 0) - && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) - return 1; - else - return 0; -} - -int -make_executable (const char *path) -{ - int rval = 0; - struct stat st; - - lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", - nonempty (path)); - if ((!path) || (!*path)) - return 0; - - if (stat (path, &st) >= 0) - { - rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); - } - return rval; -} - -/* Searches for the full path of the wrapper. Returns - newly allocated full path name if found, NULL otherwise - Does not chase symlinks, even on platforms that support them. -*/ -char * -find_executable (const char *wrapper) -{ - int has_slash = 0; - const char *p; - const char *p_next; - /* static buffer for getcwd */ - char tmp[LT_PATHMAX + 1]; - int tmp_len; - char *concat_name; - - lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", - nonempty (wrapper)); - - if ((wrapper == NULL) || (*wrapper == '\0')) - return NULL; - - /* Absolute path? */ -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') - { - concat_name = xstrdup (wrapper); - if (check_executable (concat_name)) - return concat_name; - XFREE (concat_name); - } - else - { -#endif - if (IS_DIR_SEPARATOR (wrapper[0])) - { - concat_name = xstrdup (wrapper); - if (check_executable (concat_name)) - return concat_name; - XFREE (concat_name); - } -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - } -#endif - - for (p = wrapper; *p; p++) - if (*p == '/') - { - has_slash = 1; - break; - } - if (!has_slash) - { - /* no slashes; search PATH */ - const char *path = getenv ("PATH"); - if (path != NULL) - { - for (p = path; *p; p = p_next) - { - const char *q; - size_t p_len; - for (q = p; *q; q++) - if (IS_PATH_SEPARATOR (*q)) - break; - p_len = q - p; - p_next = (*q == '\0' ? q : q + 1); - if (p_len == 0) - { - /* empty path: current directory */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", - nonnull (strerror (errno))); - tmp_len = strlen (tmp); - concat_name = - XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); - } - else - { - concat_name = - XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); - memcpy (concat_name, p, p_len); - concat_name[p_len] = '/'; - strcpy (concat_name + p_len + 1, wrapper); - } - if (check_executable (concat_name)) - return concat_name; - XFREE (concat_name); - } - } - /* not found in PATH; assume curdir */ - } - /* Relative path | not found in path: prepend cwd */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", - nonnull (strerror (errno))); - tmp_len = strlen (tmp); - concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); - - if (check_executable (concat_name)) - return concat_name; - XFREE (concat_name); - return NULL; -} - -char * -chase_symlinks (const char *pathspec) -{ -#ifndef S_ISLNK - return xstrdup (pathspec); -#else - char buf[LT_PATHMAX]; - struct stat s; - char *tmp_pathspec = xstrdup (pathspec); - char *p; - int has_symlinks = 0; - while (strlen (tmp_pathspec) && !has_symlinks) - { - lt_debugprintf (__FILE__, __LINE__, - "checking path component for symlinks: %s\n", - tmp_pathspec); - if (lstat (tmp_pathspec, &s) == 0) - { - if (S_ISLNK (s.st_mode) != 0) - { - has_symlinks = 1; - break; - } - - /* search backwards for last DIR_SEPARATOR */ - p = tmp_pathspec + strlen (tmp_pathspec) - 1; - while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) - p--; - if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) - { - /* no more DIR_SEPARATORS left */ - break; - } - *p = '\0'; - } - else - { - lt_fatal (__FILE__, __LINE__, - "error accessing file \"%s\": %s", - tmp_pathspec, nonnull (strerror (errno))); - } - } - XFREE (tmp_pathspec); - - if (!has_symlinks) - { - return xstrdup (pathspec); - } - - tmp_pathspec = realpath (pathspec, buf); - if (tmp_pathspec == 0) - { - lt_fatal (__FILE__, __LINE__, - "could not follow symlinks for %s", pathspec); - } - return xstrdup (tmp_pathspec); -#endif -} - -char * -strendzap (char *str, const char *pat) -{ - size_t len, patlen; - - assert (str != NULL); - assert (pat != NULL); - - len = strlen (str); - patlen = strlen (pat); - - if (patlen <= len) - { - str += len - patlen; - if (strcmp (str, pat) == 0) - *str = '\0'; - } - return str; -} - -void -lt_debugprintf (const char *file, int line, const char *fmt, ...) -{ - va_list args; - if (lt_debug) - { - (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); - va_start (args, fmt); - (void) vfprintf (stderr, fmt, args); - va_end (args); - } -} - -static void -lt_error_core (int exit_status, const char *file, - int line, const char *mode, - const char *message, va_list ap) -{ - fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); - vfprintf (stderr, message, ap); - fprintf (stderr, ".\n"); - - if (exit_status >= 0) - exit (exit_status); -} - -void -lt_fatal (const char *file, int line, const char *message, ...) -{ - va_list ap; - va_start (ap, message); - lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); - va_end (ap); -} - -static const char * -nonnull (const char *s) -{ - return s ? s : "(null)"; -} - -static const char * -nonempty (const char *s) -{ - return (s && !*s) ? "(empty)" : nonnull (s); -} - -void -lt_setenv (const char *name, const char *value) -{ - lt_debugprintf (__FILE__, __LINE__, - "(lt_setenv) setting '%s' to '%s'\n", - nonnull (name), nonnull (value)); - { -#ifdef HAVE_SETENV - /* always make a copy, for consistency with !HAVE_SETENV */ - char *str = xstrdup (value); - setenv (name, str, 1); -#else - int len = strlen (name) + 1 + strlen (value) + 1; - char *str = XMALLOC (char, len); - sprintf (str, "%s=%s", name, value); - if (putenv (str) != EXIT_SUCCESS) - { - XFREE (str); - } -#endif - } -} - -char * -lt_extend_str (const char *orig_value, const char *add, int to_end) -{ - char *new_value; - if (orig_value && *orig_value) - { - int orig_value_len = strlen (orig_value); - int add_len = strlen (add); - new_value = XMALLOC (char, add_len + orig_value_len + 1); - if (to_end) - { - strcpy (new_value, orig_value); - strcpy (new_value + orig_value_len, add); - } - else - { - strcpy (new_value, add); - strcpy (new_value + add_len, orig_value); - } - } - else - { - new_value = xstrdup (add); - } - return new_value; -} - -void -lt_update_exe_path (const char *name, const char *value) -{ - lt_debugprintf (__FILE__, __LINE__, - "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", - nonnull (name), nonnull (value)); - - if (name && *name && value && *value) - { - char *new_value = lt_extend_str (getenv (name), value, 0); - /* some systems can't cope with a ':'-terminated path #' */ - int len = strlen (new_value); - while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) - { - new_value[len-1] = '\0'; - } - lt_setenv (name, new_value); - XFREE (new_value); - } -} - -void -lt_update_lib_path (const char *name, const char *value) -{ - lt_debugprintf (__FILE__, __LINE__, - "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", - nonnull (name), nonnull (value)); - - if (name && *name && value && *value) - { - char *new_value = lt_extend_str (getenv (name), value, 0); - lt_setenv (name, new_value); - XFREE (new_value); - } -} - -EOF - case $host_os in - mingw*) - cat <<"EOF" - -/* Prepares an argument vector before calling spawn(). - Note that spawn() does not by itself call the command interpreter - (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : - ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&v); - v.dwPlatformId == VER_PLATFORM_WIN32_NT; - }) ? "cmd.exe" : "command.com"). - Instead it simply concatenates the arguments, separated by ' ', and calls - CreateProcess(). We must quote the arguments since Win32 CreateProcess() - interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a - special way: - - Space and tab are interpreted as delimiters. They are not treated as - delimiters if they are surrounded by double quotes: "...". - - Unescaped double quotes are removed from the input. Their only effect is - that within double quotes, space and tab are treated like normal - characters. - - Backslashes not followed by double quotes are not special. - - But 2*n+1 backslashes followed by a double quote become - n backslashes followed by a double quote (n >= 0): - \" -> " - \\\" -> \" - \\\\\" -> \\" - */ -#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" -#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" -char ** -prepare_spawn (char **argv) -{ - size_t argc; - char **new_argv; - size_t i; - - /* Count number of arguments. */ - for (argc = 0; argv[argc] != NULL; argc++) - ; - - /* Allocate new argument vector. */ - new_argv = XMALLOC (char *, argc + 1); - - /* Put quoted arguments into the new argument vector. */ - for (i = 0; i < argc; i++) - { - const char *string = argv[i]; - - if (string[0] == '\0') - new_argv[i] = xstrdup ("\"\""); - else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) - { - int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); - size_t length; - unsigned int backslashes; - const char *s; - char *quoted_string; - char *p; - - length = 0; - backslashes = 0; - if (quote_around) - length++; - for (s = string; *s != '\0'; s++) - { - char c = *s; - if (c == '"') - length += backslashes + 1; - length++; - if (c == '\\') - backslashes++; - else - backslashes = 0; - } - if (quote_around) - length += backslashes + 1; - - quoted_string = XMALLOC (char, length + 1); - - p = quoted_string; - backslashes = 0; - if (quote_around) - *p++ = '"'; - for (s = string; *s != '\0'; s++) - { - char c = *s; - if (c == '"') - { - unsigned int j; - for (j = backslashes + 1; j > 0; j--) - *p++ = '\\'; - } - *p++ = c; - if (c == '\\') - backslashes++; - else - backslashes = 0; - } - if (quote_around) - { - unsigned int j; - for (j = backslashes; j > 0; j--) - *p++ = '\\'; - *p++ = '"'; - } - *p = '\0'; - - new_argv[i] = quoted_string; - } - else - new_argv[i] = (char *) string; - } - new_argv[argc] = NULL; - - return new_argv; -} -EOF - ;; - esac - - cat <<"EOF" -void lt_dump_script (FILE* f) -{ -EOF - func_emit_wrapper yes | - $SED -n -e ' -s/^\(.\{79\}\)\(..*\)/\1\ -\2/ -h -s/\([\\"]\)/\\\1/g -s/$/\\n/ -s/\([^\n]*\).*/ fputs ("\1", f);/p -g -D' - cat <<"EOF" -} -EOF -} -# end: func_emit_cwrapperexe_src - -# func_win32_import_lib_p ARG -# True if ARG is an import lib, as indicated by $file_magic_cmd -func_win32_import_lib_p () -{ - $opt_debug - case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in - *import*) : ;; - *) false ;; - esac -} - -# func_mode_link arg... -func_mode_link () -{ - $opt_debug - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) - # It is impossible to link a dll without this setting, and - # we shouldn't force the makefile maintainer to figure out - # which system we are compiling for in order to pass an extra - # flag for every libtool invocation. - # allow_undefined=no - - # FIXME: Unfortunately, there are problems with the above when trying - # to make a dll which has undefined symbols, in which case not - # even a static library is built. For now, we need to specify - # -no-undefined on the libtool link line when we can be certain - # that all symbols are satisfied, otherwise we get a static library. - allow_undefined=yes - ;; - *) - allow_undefined=yes - ;; - esac - libtool_args=$nonopt - base_compile="$nonopt $@" - compile_command=$nonopt - finalize_command=$nonopt - - compile_rpath= - finalize_rpath= - compile_shlibpath= - finalize_shlibpath= - convenience= - old_convenience= - deplibs= - old_deplibs= - compiler_flags= - linker_flags= - dllsearchpath= - lib_search_path=`pwd` - inst_prefix_dir= - new_inherited_linker_flags= - - avoid_version=no - bindir= - dlfiles= - dlprefiles= - dlself=no - export_dynamic=no - export_symbols= - export_symbols_regex= - generated= - libobjs= - ltlibs= - module=no - no_install=no - objs= - non_pic_objects= - precious_files_regex= - prefer_static_libs=no - preload=no - prev= - prevarg= - release= - rpath= - xrpath= - perm_rpath= - temp_rpath= - thread_safe=no - vinfo= - vinfo_number=no - weak_libs= - single_module="${wl}-single_module" - func_infer_tag $base_compile - - # We need to know -static, to get the right output filenames. - for arg - do - case $arg in - -shared) - test "$build_libtool_libs" != yes && \ - func_fatal_configuration "can not build a shared library" - build_old_libs=no - break - ;; - -all-static | -static | -static-libtool-libs) - case $arg in - -all-static) - if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then - func_warning "complete static linking is impossible in this configuration" - fi - if test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=yes - ;; - -static) - if test -z "$pic_flag" && test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=built - ;; - -static-libtool-libs) - if test -z "$pic_flag" && test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=yes - ;; - esac - build_libtool_libs=no - build_old_libs=yes - break - ;; - esac - done - - # See if our shared archives depend on static archives. - test -n "$old_archive_from_new_cmds" && build_old_libs=yes - - # Go through the arguments, transforming them on the way. - while test "$#" -gt 0; do - arg="$1" - shift - func_quote_for_eval "$arg" - qarg=$func_quote_for_eval_unquoted_result - func_append libtool_args " $func_quote_for_eval_result" - - # If the previous option needs an argument, assign it. - if test -n "$prev"; then - case $prev in - output) - func_append compile_command " @OUTPUT@" - func_append finalize_command " @OUTPUT@" - ;; - esac - - case $prev in - bindir) - bindir="$arg" - prev= - continue - ;; - dlfiles|dlprefiles) - if test "$preload" = no; then - # Add the symbol object into the linking commands. - func_append compile_command " @SYMFILE@" - func_append finalize_command " @SYMFILE@" - preload=yes - fi - case $arg in - *.la | *.lo) ;; # We handle these cases below. - force) - if test "$dlself" = no; then - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - self) - if test "$prev" = dlprefiles; then - dlself=yes - elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then - dlself=yes - else - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - *) - if test "$prev" = dlfiles; then - func_append dlfiles " $arg" - else - func_append dlprefiles " $arg" - fi - prev= - continue - ;; - esac - ;; - expsyms) - export_symbols="$arg" - test -f "$arg" \ - || func_fatal_error "symbol file \`$arg' does not exist" - prev= - continue - ;; - expsyms_regex) - export_symbols_regex="$arg" - prev= - continue - ;; - framework) - case $host in - *-*-darwin*) - case "$deplibs " in - *" $qarg.ltframework "*) ;; - *) func_append deplibs " $qarg.ltframework" # this is fixed later - ;; - esac - ;; - esac - prev= - continue - ;; - inst_prefix) - inst_prefix_dir="$arg" - prev= - continue - ;; - objectlist) - if test -f "$arg"; then - save_arg=$arg - moreargs= - for fil in `cat "$save_arg"` - do -# func_append moreargs " $fil" - arg=$fil - # A libtool-controlled object. - - # Check to see that this really is a libtool object. - if func_lalib_unsafe_p "$arg"; then - pic_object= - non_pic_object= - - # Read the .lo file - func_source "$arg" - - if test -z "$pic_object" || - test -z "$non_pic_object" || - test "$pic_object" = none && - test "$non_pic_object" = none; then - func_fatal_error "cannot find name of object for \`$arg'" - fi - - # Extract subdirectory from the argument. - func_dirname "$arg" "/" "" - xdir="$func_dirname_result" - - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" - - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - func_append dlfiles " $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi - - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - func_append dlprefiles " $pic_object" - prev= - fi - - # A PIC object. - func_append libobjs " $pic_object" - arg="$pic_object" - fi - - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" - - # A standard non-PIC object - func_append non_pic_objects " $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - func_append non_pic_objects " $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if $opt_dry_run; then - # Extract subdirectory from the argument. - func_dirname "$arg" "/" "" - xdir="$func_dirname_result" - - func_lo2o "$arg" - pic_object=$xdir$objdir/$func_lo2o_result - non_pic_object=$xdir$func_lo2o_result - func_append libobjs " $pic_object" - func_append non_pic_objects " $non_pic_object" - else - func_fatal_error "\`$arg' is not a valid libtool object" - fi - fi - done - else - func_fatal_error "link input file \`$arg' does not exist" - fi - arg=$save_arg - prev= - continue - ;; - precious_regex) - precious_files_regex="$arg" - prev= - continue - ;; - release) - release="-$arg" - prev= - continue - ;; - rpath | xrpath) - # We need an absolute path. - case $arg in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - func_fatal_error "only absolute run-paths are allowed" - ;; - esac - if test "$prev" = rpath; then - case "$rpath " in - *" $arg "*) ;; - *) func_append rpath " $arg" ;; - esac - else - case "$xrpath " in - *" $arg "*) ;; - *) func_append xrpath " $arg" ;; - esac - fi - prev= - continue - ;; - shrext) - shrext_cmds="$arg" - prev= - continue - ;; - weak) - func_append weak_libs " $arg" - prev= - continue - ;; - xcclinker) - func_append linker_flags " $qarg" - func_append compiler_flags " $qarg" - prev= - func_append compile_command " $qarg" - func_append finalize_command " $qarg" - continue - ;; - xcompiler) - func_append compiler_flags " $qarg" - prev= - func_append compile_command " $qarg" - func_append finalize_command " $qarg" - continue - ;; - xlinker) - func_append linker_flags " $qarg" - func_append compiler_flags " $wl$qarg" - prev= - func_append compile_command " $wl$qarg" - func_append finalize_command " $wl$qarg" - continue - ;; - *) - eval "$prev=\"\$arg\"" - prev= - continue - ;; - esac - fi # test -n "$prev" - - prevarg="$arg" - - case $arg in - -all-static) - if test -n "$link_static_flag"; then - # See comment for -static flag below, for more details. - func_append compile_command " $link_static_flag" - func_append finalize_command " $link_static_flag" - fi - continue - ;; - - -allow-undefined) - # FIXME: remove this flag sometime in the future. - func_fatal_error "\`-allow-undefined' must not be used because it is the default" - ;; - - -avoid-version) - avoid_version=yes - continue - ;; - - -bindir) - prev=bindir - continue - ;; - - -dlopen) - prev=dlfiles - continue - ;; - - -dlpreopen) - prev=dlprefiles - continue - ;; - - -export-dynamic) - export_dynamic=yes - continue - ;; - - -export-symbols | -export-symbols-regex) - if test -n "$export_symbols" || test -n "$export_symbols_regex"; then - func_fatal_error "more than one -exported-symbols argument is not allowed" - fi - if test "X$arg" = "X-export-symbols"; then - prev=expsyms - else - prev=expsyms_regex - fi - continue - ;; - - -framework) - prev=framework - continue - ;; - - -inst-prefix-dir) - prev=inst_prefix - continue - ;; - - # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* - # so, if we see these flags be careful not to treat them like -L - -L[A-Z][A-Z]*:*) - case $with_gcc/$host in - no/*-*-irix* | /*-*-irix*) - func_append compile_command " $arg" - func_append finalize_command " $arg" - ;; - esac - continue - ;; - - -L*) - func_stripname "-L" '' "$arg" - if test -z "$func_stripname_result"; then - if test "$#" -gt 0; then - func_fatal_error "require no space between \`-L' and \`$1'" - else - func_fatal_error "need path for \`-L' option" - fi - fi - func_resolve_sysroot "$func_stripname_result" - dir=$func_resolve_sysroot_result - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - absdir=`cd "$dir" && pwd` - test -z "$absdir" && \ - func_fatal_error "cannot determine absolute directory name of \`$dir'" - dir="$absdir" - ;; - esac - case "$deplibs " in - *" -L$dir "* | *" $arg "*) - # Will only happen for absolute or sysroot arguments - ;; - *) - # Preserve sysroot, but never include relative directories - case $dir in - [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; - *) func_append deplibs " -L$dir" ;; - esac - func_append lib_search_path " $dir" - ;; - esac - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) - testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$dir:"*) ;; - ::) dllsearchpath=$dir;; - *) func_append dllsearchpath ":$dir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - ::) dllsearchpath=$testbindir;; - *) func_append dllsearchpath ":$testbindir";; - esac - ;; - esac - continue - ;; - - -l*) - if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) - # These systems don't actually have a C or math library (as such) - continue - ;; - *-*-os2*) - # These systems don't actually have a C library (as such) - test "X$arg" = "X-lc" && continue - ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc due to us having libc/libc_r. - test "X$arg" = "X-lc" && continue - ;; - *-*-rhapsody* | *-*-darwin1.[012]) - # Rhapsody C and math libraries are in the System framework - func_append deplibs " System.ltframework" - continue - ;; - *-*-sco3.2v5* | *-*-sco5v6*) - # Causes problems with __ctype - test "X$arg" = "X-lc" && continue - ;; - *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) - # Compiler inserts libc in the correct place for threads to work - test "X$arg" = "X-lc" && continue - ;; - esac - elif test "X$arg" = "X-lc_r"; then - case $host in - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc_r directly, use -pthread flag. - continue - ;; - esac - fi - func_append deplibs " $arg" - continue - ;; - - -module) - module=yes - continue - ;; - - # Tru64 UNIX uses -model [arg] to determine the layout of C++ - # classes, name mangling, and exception handling. - # Darwin uses the -arch flag to determine output architecture. - -model|-arch|-isysroot|--sysroot) - func_append compiler_flags " $arg" - func_append compile_command " $arg" - func_append finalize_command " $arg" - prev=xcompiler - continue - ;; - - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ - |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) - func_append compiler_flags " $arg" - func_append compile_command " $arg" - func_append finalize_command " $arg" - case "$new_inherited_linker_flags " in - *" $arg "*) ;; - * ) func_append new_inherited_linker_flags " $arg" ;; - esac - continue - ;; - - -multi_module) - single_module="${wl}-multi_module" - continue - ;; - - -no-fast-install) - fast_install=no - continue - ;; - - -no-install) - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) - # The PATH hackery in wrapper scripts is required on Windows - # and Darwin in order for the loader to find any dlls it needs. - func_warning "\`-no-install' is ignored for $host" - func_warning "assuming \`-no-fast-install' instead" - fast_install=no - ;; - *) no_install=yes ;; - esac - continue - ;; - - -no-undefined) - allow_undefined=no - continue - ;; - - -objectlist) - prev=objectlist - continue - ;; - - -o) prev=output ;; - - -precious-files-regex) - prev=precious_regex - continue - ;; - - -release) - prev=release - continue - ;; - - -rpath) - prev=rpath - continue - ;; - - -R) - prev=xrpath - continue - ;; - - -R*) - func_stripname '-R' '' "$arg" - dir=$func_stripname_result - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - =*) - func_stripname '=' '' "$dir" - dir=$lt_sysroot$func_stripname_result - ;; - *) - func_fatal_error "only absolute run-paths are allowed" - ;; - esac - case "$xrpath " in - *" $dir "*) ;; - *) func_append xrpath " $dir" ;; - esac - continue - ;; - - -shared) - # The effects of -shared are defined in a previous loop. - continue - ;; - - -shrext) - prev=shrext - continue - ;; - - -static | -static-libtool-libs) - # The effects of -static are defined in a previous loop. - # We used to do the same as -all-static on platforms that - # didn't have a PIC flag, but the assumption that the effects - # would be equivalent was wrong. It would break on at least - # Digital Unix and AIX. - continue - ;; - - -thread-safe) - thread_safe=yes - continue - ;; - - -version-info) - prev=vinfo - continue - ;; - - -version-number) - prev=vinfo - vinfo_number=yes - continue - ;; - - -weak) - prev=weak - continue - ;; - - -Wc,*) - func_stripname '-Wc,' '' "$arg" - args=$func_stripname_result - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - func_quote_for_eval "$flag" - func_append arg " $func_quote_for_eval_result" - func_append compiler_flags " $func_quote_for_eval_result" - done - IFS="$save_ifs" - func_stripname ' ' '' "$arg" - arg=$func_stripname_result - ;; - - -Wl,*) - func_stripname '-Wl,' '' "$arg" - args=$func_stripname_result - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - func_quote_for_eval "$flag" - func_append arg " $wl$func_quote_for_eval_result" - func_append compiler_flags " $wl$func_quote_for_eval_result" - func_append linker_flags " $func_quote_for_eval_result" - done - IFS="$save_ifs" - func_stripname ' ' '' "$arg" - arg=$func_stripname_result - ;; - - -Xcompiler) - prev=xcompiler - continue - ;; - - -Xlinker) - prev=xlinker - continue - ;; - - -XCClinker) - prev=xcclinker - continue - ;; - - # -msg_* for osf cc - -msg_*) - func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" - ;; - - # Flags to be passed through unchanged, with rationale: - # -64, -mips[0-9] enable 64-bit mode for the SGI compiler - # -r[0-9][0-9]* specify processor for the SGI compiler - # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler - # +DA*, +DD* enable 64-bit mode for the HP compiler - # -q* compiler args for the IBM compiler - # -m*, -t[45]*, -txscale* architecture-specific flags for GCC - # -F/path path to uninstalled frameworks, gcc on darwin - # -p, -pg, --coverage, -fprofile-* profiling flags for GCC - # @file GCC response files - # -tp=* Portland pgcc target processor selection - # --sysroot=* for sysroot support - # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization - -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ - -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ - -O*|-flto*|-fwhopr*|-fuse-linker-plugin) - func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" - func_append compile_command " $arg" - func_append finalize_command " $arg" - func_append compiler_flags " $arg" - continue - ;; - - # Some other compiler flag. - -* | +*) - func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" - ;; - - *.$objext) - # A standard object. - func_append objs " $arg" - ;; - - *.lo) - # A libtool-controlled object. - - # Check to see that this really is a libtool object. - if func_lalib_unsafe_p "$arg"; then - pic_object= - non_pic_object= - - # Read the .lo file - func_source "$arg" - - if test -z "$pic_object" || - test -z "$non_pic_object" || - test "$pic_object" = none && - test "$non_pic_object" = none; then - func_fatal_error "cannot find name of object for \`$arg'" - fi - - # Extract subdirectory from the argument. - func_dirname "$arg" "/" "" - xdir="$func_dirname_result" - - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" - - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - func_append dlfiles " $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi - - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - func_append dlprefiles " $pic_object" - prev= - fi - - # A PIC object. - func_append libobjs " $pic_object" - arg="$pic_object" - fi - - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" - - # A standard non-PIC object - func_append non_pic_objects " $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - func_append non_pic_objects " $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if $opt_dry_run; then - # Extract subdirectory from the argument. - func_dirname "$arg" "/" "" - xdir="$func_dirname_result" - - func_lo2o "$arg" - pic_object=$xdir$objdir/$func_lo2o_result - non_pic_object=$xdir$func_lo2o_result - func_append libobjs " $pic_object" - func_append non_pic_objects " $non_pic_object" - else - func_fatal_error "\`$arg' is not a valid libtool object" - fi - fi - ;; - - *.$libext) - # An archive. - func_append deplibs " $arg" - func_append old_deplibs " $arg" - continue - ;; - - *.la) - # A libtool-controlled library. - - func_resolve_sysroot "$arg" - if test "$prev" = dlfiles; then - # This library was specified with -dlopen. - func_append dlfiles " $func_resolve_sysroot_result" - prev= - elif test "$prev" = dlprefiles; then - # The library was specified with -dlpreopen. - func_append dlprefiles " $func_resolve_sysroot_result" - prev= - else - func_append deplibs " $func_resolve_sysroot_result" - fi - continue - ;; - - # Some other compiler argument. - *) - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" - ;; - esac # arg - - # Now actually substitute the argument into the commands. - if test -n "$arg"; then - func_append compile_command " $arg" - func_append finalize_command " $arg" - fi - done # argument parsing loop - - test -n "$prev" && \ - func_fatal_help "the \`$prevarg' option requires an argument" - - if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then - eval arg=\"$export_dynamic_flag_spec\" - func_append compile_command " $arg" - func_append finalize_command " $arg" - fi - - oldlibs= - # calculate the name of the file, without its directory - func_basename "$output" - outputname="$func_basename_result" - libobjs_save="$libobjs" - - if test -n "$shlibpath_var"; then - # get the directories listed in $shlibpath_var - eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` - else - shlib_search_path= - fi - eval sys_lib_search_path=\"$sys_lib_search_path_spec\" - eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" - - func_dirname "$output" "/" "" - output_objdir="$func_dirname_result$objdir" - func_to_tool_file "$output_objdir/" - tool_output_objdir=$func_to_tool_file_result - # Create the object directory. - func_mkdir_p "$output_objdir" - - # Determine the type of output - case $output in - "") - func_fatal_help "you must specify an output file" - ;; - *.$libext) linkmode=oldlib ;; - *.lo | *.$objext) linkmode=obj ;; - *.la) linkmode=lib ;; - *) linkmode=prog ;; # Anything else should be a program. - esac - - specialdeplibs= - - libs= - # Find all interdependent deplibs by searching for libraries - # that are linked more than once (e.g. -la -lb -la) - for deplib in $deplibs; do - if $opt_preserve_dup_deps ; then - case "$libs " in - *" $deplib "*) func_append specialdeplibs " $deplib" ;; - esac - fi - func_append libs " $deplib" - done - - if test "$linkmode" = lib; then - libs="$predeps $libs $compiler_lib_search_path $postdeps" - - # Compute libraries that are listed more than once in $predeps - # $postdeps and mark them as special (i.e., whose duplicates are - # not to be eliminated). - pre_post_deps= - if $opt_duplicate_compiler_generated_deps; then - for pre_post_dep in $predeps $postdeps; do - case "$pre_post_deps " in - *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; - esac - func_append pre_post_deps " $pre_post_dep" - done - fi - pre_post_deps= - fi - - deplibs= - newdependency_libs= - newlib_search_path= - need_relink=no # whether we're linking any uninstalled libtool libraries - notinst_deplibs= # not-installed libtool libraries - notinst_path= # paths that contain not-installed libtool libraries - - case $linkmode in - lib) - passes="conv dlpreopen link" - for file in $dlfiles $dlprefiles; do - case $file in - *.la) ;; - *) - func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" - ;; - esac - done - ;; - prog) - compile_deplibs= - finalize_deplibs= - alldeplibs=no - newdlfiles= - newdlprefiles= - passes="conv scan dlopen dlpreopen link" - ;; - *) passes="conv" - ;; - esac - - for pass in $passes; do - # The preopen pass in lib mode reverses $deplibs; put it back here - # so that -L comes before libs that need it for instance... - if test "$linkmode,$pass" = "lib,link"; then - ## FIXME: Find the place where the list is rebuilt in the wrong - ## order, and fix it there properly - tmp_deplibs= - for deplib in $deplibs; do - tmp_deplibs="$deplib $tmp_deplibs" - done - deplibs="$tmp_deplibs" - fi - - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan"; then - libs="$deplibs" - deplibs= - fi - if test "$linkmode" = prog; then - case $pass in - dlopen) libs="$dlfiles" ;; - dlpreopen) libs="$dlprefiles" ;; - link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; - esac - fi - if test "$linkmode,$pass" = "lib,dlpreopen"; then - # Collect and forward deplibs of preopened libtool libs - for lib in $dlprefiles; do - # Ignore non-libtool-libs - dependency_libs= - func_resolve_sysroot "$lib" - case $lib in - *.la) func_source "$func_resolve_sysroot_result" ;; - esac - - # Collect preopened libtool deplibs, except any this library - # has declared as weak libs - for deplib in $dependency_libs; do - func_basename "$deplib" - deplib_base=$func_basename_result - case " $weak_libs " in - *" $deplib_base "*) ;; - *) func_append deplibs " $deplib" ;; - esac - done - done - libs="$dlprefiles" - fi - if test "$pass" = dlopen; then - # Collect dlpreopened libraries - save_deplibs="$deplibs" - deplibs= - fi - - for deplib in $libs; do - lib= - found=no - case $deplib in - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ - |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - func_append compiler_flags " $deplib" - if test "$linkmode" = lib ; then - case "$new_inherited_linker_flags " in - *" $deplib "*) ;; - * ) func_append new_inherited_linker_flags " $deplib" ;; - esac - fi - fi - continue - ;; - -l*) - if test "$linkmode" != lib && test "$linkmode" != prog; then - func_warning "\`-l' is ignored for archives/objects" - continue - fi - func_stripname '-l' '' "$deplib" - name=$func_stripname_result - if test "$linkmode" = lib; then - searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" - else - searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" - fi - for searchdir in $searchdirs; do - for search_ext in .la $std_shrext .so .a; do - # Search the libtool library - lib="$searchdir/lib${name}${search_ext}" - if test -f "$lib"; then - if test "$search_ext" = ".la"; then - found=yes - else - found=no - fi - break 2 - fi - done - done - if test "$found" != yes; then - # deplib doesn't seem to be a libtool library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - else # deplib is a libtool library - # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, - # We need to do some special things here, and not later. - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $deplib "*) - if func_lalib_p "$lib"; then - library_names= - old_library= - func_source "$lib" - for l in $old_library $library_names; do - ll="$l" - done - if test "X$ll" = "X$old_library" ; then # only static version available - found=no - func_dirname "$lib" "" "." - ladir="$func_dirname_result" - lib=$ladir/$old_library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - fi - fi - ;; - *) ;; - esac - fi - fi - ;; # -l - *.ltframework) - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - if test "$linkmode" = lib ; then - case "$new_inherited_linker_flags " in - *" $deplib "*) ;; - * ) func_append new_inherited_linker_flags " $deplib" ;; - esac - fi - fi - continue - ;; - -L*) - case $linkmode in - lib) - deplibs="$deplib $deplibs" - test "$pass" = conv && continue - newdependency_libs="$deplib $newdependency_libs" - func_stripname '-L' '' "$deplib" - func_resolve_sysroot "$func_stripname_result" - func_append newlib_search_path " $func_resolve_sysroot_result" - ;; - prog) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - if test "$pass" = scan; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - func_stripname '-L' '' "$deplib" - func_resolve_sysroot "$func_stripname_result" - func_append newlib_search_path " $func_resolve_sysroot_result" - ;; - *) - func_warning "\`-L' is ignored for archives/objects" - ;; - esac # linkmode - continue - ;; # -L - -R*) - if test "$pass" = link; then - func_stripname '-R' '' "$deplib" - func_resolve_sysroot "$func_stripname_result" - dir=$func_resolve_sysroot_result - # Make sure the xrpath contains only unique directories. - case "$xrpath " in - *" $dir "*) ;; - *) func_append xrpath " $dir" ;; - esac - fi - deplibs="$deplib $deplibs" - continue - ;; - *.la) - func_resolve_sysroot "$deplib" - lib=$func_resolve_sysroot_result - ;; - *.$libext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - case $linkmode in - lib) - # Linking convenience modules into shared libraries is allowed, - # but linking other static libraries is non-portable. - case " $dlpreconveniencelibs " in - *" $deplib "*) ;; - *) - valid_a_lib=no - case $deplibs_check_method in - match_pattern*) - set dummy $deplibs_check_method; shift - match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` - if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ - | $EGREP "$match_pattern_regex" > /dev/null; then - valid_a_lib=yes - fi - ;; - pass_all) - valid_a_lib=yes - ;; - esac - if test "$valid_a_lib" != yes; then - echo - $ECHO "*** Warning: Trying to link with static lib archive $deplib." - echo "*** I have the capability to make that library automatically link in when" - echo "*** you link to this library. But I can only do this if you have a" - echo "*** shared version of the library, which you do not appear to have" - echo "*** because the file extensions .$libext of this argument makes me believe" - echo "*** that it is just a static archive that I should not use here." - else - echo - $ECHO "*** Warning: Linking the shared library $output against the" - $ECHO "*** static library $deplib is not portable!" - deplibs="$deplib $deplibs" - fi - ;; - esac - continue - ;; - prog) - if test "$pass" != link; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - continue - ;; - esac # linkmode - ;; # *.$libext - *.lo | *.$objext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - elif test "$linkmode" = prog; then - if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then - # If there is no dlopen support or we're linking statically, - # we need to preload. - func_append newdlprefiles " $deplib" - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - func_append newdlfiles " $deplib" - fi - fi - continue - ;; - %DEPLIBS%) - alldeplibs=yes - continue - ;; - esac # case $deplib - - if test "$found" = yes || test -f "$lib"; then : - else - func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" - fi - - # Check to see that this really is a libtool archive. - func_lalib_unsafe_p "$lib" \ - || func_fatal_error "\`$lib' is not a valid libtool archive" - - func_dirname "$lib" "" "." - ladir="$func_dirname_result" - - dlname= - dlopen= - dlpreopen= - libdir= - library_names= - old_library= - inherited_linker_flags= - # If the library was installed with an old release of libtool, - # it will not redefine variables installed, or shouldnotlink - installed=yes - shouldnotlink=no - avoidtemprpath= - - - # Read the .la file - func_source "$lib" - - # Convert "-framework foo" to "foo.ltframework" - if test -n "$inherited_linker_flags"; then - tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` - for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do - case " $new_inherited_linker_flags " in - *" $tmp_inherited_linker_flag "*) ;; - *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; - esac - done - fi - dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan" || - { test "$linkmode" != prog && test "$linkmode" != lib; }; then - test -n "$dlopen" && func_append dlfiles " $dlopen" - test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" - fi - - if test "$pass" = conv; then - # Only check for convenience libraries - deplibs="$lib $deplibs" - if test -z "$libdir"; then - if test -z "$old_library"; then - func_fatal_error "cannot find name of link library for \`$lib'" - fi - # It is a libtool convenience library, so add in its objects. - func_append convenience " $ladir/$objdir/$old_library" - func_append old_convenience " $ladir/$objdir/$old_library" - elif test "$linkmode" != prog && test "$linkmode" != lib; then - func_fatal_error "\`$lib' is not a convenience library" - fi - tmp_libs= - for deplib in $dependency_libs; do - deplibs="$deplib $deplibs" - if $opt_preserve_dup_deps ; then - case "$tmp_libs " in - *" $deplib "*) func_append specialdeplibs " $deplib" ;; - esac - fi - func_append tmp_libs " $deplib" - done - continue - fi # $pass = conv - - - # Get the name of the library we link against. - linklib= - if test -n "$old_library" && - { test "$prefer_static_libs" = yes || - test "$prefer_static_libs,$installed" = "built,no"; }; then - linklib=$old_library - else - for l in $old_library $library_names; do - linklib="$l" - done - fi - if test -z "$linklib"; then - func_fatal_error "cannot find name of link library for \`$lib'" - fi - - # This library was specified with -dlopen. - if test "$pass" = dlopen; then - if test -z "$libdir"; then - func_fatal_error "cannot -dlopen a convenience library: \`$lib'" - fi - if test -z "$dlname" || - test "$dlopen_support" != yes || - test "$build_libtool_libs" = no; then - # If there is no dlname, no dlopen support or we're linking - # statically, we need to preload. We also need to preload any - # dependent libraries so libltdl's deplib preloader doesn't - # bomb out in the load deplibs phase. - func_append dlprefiles " $lib $dependency_libs" - else - func_append newdlfiles " $lib" - fi - continue - fi # $pass = dlopen - - # We need an absolute path. - case $ladir in - [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; - *) - abs_ladir=`cd "$ladir" && pwd` - if test -z "$abs_ladir"; then - func_warning "cannot determine absolute directory name of \`$ladir'" - func_warning "passing it literally to the linker, although it might fail" - abs_ladir="$ladir" - fi - ;; - esac - func_basename "$lib" - laname="$func_basename_result" - - # Find the relevant object directory and library name. - if test "X$installed" = Xyes; then - if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then - func_warning "library \`$lib' was moved." - dir="$ladir" - absdir="$abs_ladir" - libdir="$abs_ladir" - else - dir="$lt_sysroot$libdir" - absdir="$lt_sysroot$libdir" - fi - test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes - else - if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then - dir="$ladir" - absdir="$abs_ladir" - # Remove this search path later - func_append notinst_path " $abs_ladir" - else - dir="$ladir/$objdir" - absdir="$abs_ladir/$objdir" - # Remove this search path later - func_append notinst_path " $abs_ladir" - fi - fi # $installed = yes - func_stripname 'lib' '.la' "$laname" - name=$func_stripname_result - - # This library was specified with -dlpreopen. - if test "$pass" = dlpreopen; then - if test -z "$libdir" && test "$linkmode" = prog; then - func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" - fi - case "$host" in - # special handling for platforms with PE-DLLs. - *cygwin* | *mingw* | *cegcc* ) - # Linker will automatically link against shared library if both - # static and shared are present. Therefore, ensure we extract - # symbols from the import library if a shared library is present - # (otherwise, the dlopen module name will be incorrect). We do - # this by putting the import library name into $newdlprefiles. - # We recover the dlopen module name by 'saving' the la file - # name in a special purpose variable, and (later) extracting the - # dlname from the la file. - if test -n "$dlname"; then - func_tr_sh "$dir/$linklib" - eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" - func_append newdlprefiles " $dir/$linklib" - else - func_append newdlprefiles " $dir/$old_library" - # Keep a list of preopened convenience libraries to check - # that they are being used correctly in the link pass. - test -z "$libdir" && \ - func_append dlpreconveniencelibs " $dir/$old_library" - fi - ;; - * ) - # Prefer using a static library (so that no silly _DYNAMIC symbols - # are required to link). - if test -n "$old_library"; then - func_append newdlprefiles " $dir/$old_library" - # Keep a list of preopened convenience libraries to check - # that they are being used correctly in the link pass. - test -z "$libdir" && \ - func_append dlpreconveniencelibs " $dir/$old_library" - # Otherwise, use the dlname, so that lt_dlopen finds it. - elif test -n "$dlname"; then - func_append newdlprefiles " $dir/$dlname" - else - func_append newdlprefiles " $dir/$linklib" - fi - ;; - esac - fi # $pass = dlpreopen - - if test -z "$libdir"; then - # Link the convenience library - if test "$linkmode" = lib; then - deplibs="$dir/$old_library $deplibs" - elif test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$dir/$old_library $compile_deplibs" - finalize_deplibs="$dir/$old_library $finalize_deplibs" - else - deplibs="$lib $deplibs" # used for prog,scan pass - fi - continue - fi - - - if test "$linkmode" = prog && test "$pass" != link; then - func_append newlib_search_path " $ladir" - deplibs="$lib $deplibs" - - linkalldeplibs=no - if test "$link_all_deplibs" != no || test -z "$library_names" || - test "$build_libtool_libs" = no; then - linkalldeplibs=yes - fi - - tmp_libs= - for deplib in $dependency_libs; do - case $deplib in - -L*) func_stripname '-L' '' "$deplib" - func_resolve_sysroot "$func_stripname_result" - func_append newlib_search_path " $func_resolve_sysroot_result" - ;; - esac - # Need to link against all dependency_libs? - if test "$linkalldeplibs" = yes; then - deplibs="$deplib $deplibs" - else - # Need to hardcode shared library paths - # or/and link against static libraries - newdependency_libs="$deplib $newdependency_libs" - fi - if $opt_preserve_dup_deps ; then - case "$tmp_libs " in - *" $deplib "*) func_append specialdeplibs " $deplib" ;; - esac - fi - func_append tmp_libs " $deplib" - done # for deplib - continue - fi # $linkmode = prog... - - if test "$linkmode,$pass" = "prog,link"; then - if test -n "$library_names" && - { { test "$prefer_static_libs" = no || - test "$prefer_static_libs,$installed" = "built,yes"; } || - test -z "$old_library"; }; then - # We need to hardcode the library path - if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then - # Make sure the rpath contains only unique directories. - case "$temp_rpath:" in - *"$absdir:"*) ;; - *) func_append temp_rpath "$absdir:" ;; - esac - fi - - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) func_append compile_rpath " $absdir" ;; - esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) func_append finalize_rpath " $libdir" ;; - esac - ;; - esac - fi # $linkmode,$pass = prog,link... - - if test "$alldeplibs" = yes && - { test "$deplibs_check_method" = pass_all || - { test "$build_libtool_libs" = yes && - test -n "$library_names"; }; }; then - # We only need to search for static libraries - continue - fi - fi - - link_static=no # Whether the deplib will be linked statically - use_static_libs=$prefer_static_libs - if test "$use_static_libs" = built && test "$installed" = yes; then - use_static_libs=no - fi - if test -n "$library_names" && - { test "$use_static_libs" = no || test -z "$old_library"; }; then - case $host in - *cygwin* | *mingw* | *cegcc*) - # No point in relinking DLLs because paths are not encoded - func_append notinst_deplibs " $lib" - need_relink=no - ;; - *) - if test "$installed" = no; then - func_append notinst_deplibs " $lib" - need_relink=yes - fi - ;; - esac - # This is a shared library - - # Warn about portability, can't link against -module's on some - # systems (darwin). Don't bleat about dlopened modules though! - dlopenmodule="" - for dlpremoduletest in $dlprefiles; do - if test "X$dlpremoduletest" = "X$lib"; then - dlopenmodule="$dlpremoduletest" - break - fi - done - if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then - echo - if test "$linkmode" = prog; then - $ECHO "*** Warning: Linking the executable $output against the loadable module" - else - $ECHO "*** Warning: Linking the shared library $output against the loadable module" - fi - $ECHO "*** $linklib is not portable!" - fi - if test "$linkmode" = lib && - test "$hardcode_into_libs" = yes; then - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) func_append compile_rpath " $absdir" ;; - esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) func_append finalize_rpath " $libdir" ;; - esac - ;; - esac - fi - - if test -n "$old_archive_from_expsyms_cmds"; then - # figure out the soname - set dummy $library_names - shift - realname="$1" - shift - libname=`eval "\\$ECHO \"$libname_spec\""` - # use dlname if we got it. it's perfectly good, no? - if test -n "$dlname"; then - soname="$dlname" - elif test -n "$soname_spec"; then - # bleh windows - case $host in - *cygwin* | mingw* | *cegcc*) - func_arith $current - $age - major=$func_arith_result - versuffix="-$major" - ;; - esac - eval soname=\"$soname_spec\" - else - soname="$realname" - fi - - # Make a new name for the extract_expsyms_cmds to use - soroot="$soname" - func_basename "$soroot" - soname="$func_basename_result" - func_stripname 'lib' '.dll' "$soname" - newlib=libimp-$func_stripname_result.a - - # If the library has no export list, then create one now - if test -f "$output_objdir/$soname-def"; then : - else - func_verbose "extracting exported symbol list from \`$soname'" - func_execute_cmds "$extract_expsyms_cmds" 'exit $?' - fi - - # Create $newlib - if test -f "$output_objdir/$newlib"; then :; else - func_verbose "generating import library for \`$soname'" - func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' - fi - # make sure the library variables are pointing to the new library - dir=$output_objdir - linklib=$newlib - fi # test -n "$old_archive_from_expsyms_cmds" - - if test "$linkmode" = prog || test "$opt_mode" != relink; then - add_shlibpath= - add_dir= - add= - lib_linked=yes - case $hardcode_action in - immediate | unsupported) - if test "$hardcode_direct" = no; then - add="$dir/$linklib" - case $host in - *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; - *-*-sysv4*uw2*) add_dir="-L$dir" ;; - *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ - *-*-unixware7*) add_dir="-L$dir" ;; - *-*-darwin* ) - # if the lib is a (non-dlopened) module then we can not - # link against it, someone is ignoring the earlier warnings - if /usr/bin/file -L $add 2> /dev/null | - $GREP ": [^:]* bundle" >/dev/null ; then - if test "X$dlopenmodule" != "X$lib"; then - $ECHO "*** Warning: lib $linklib is a module, not a shared library" - if test -z "$old_library" ; then - echo - echo "*** And there doesn't seem to be a static archive available" - echo "*** The link will probably fail, sorry" - else - add="$dir/$old_library" - fi - elif test -n "$old_library"; then - add="$dir/$old_library" - fi - fi - esac - elif test "$hardcode_minus_L" = no; then - case $host in - *-*-sunos*) add_shlibpath="$dir" ;; - esac - add_dir="-L$dir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = no; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - relink) - if test "$hardcode_direct" = yes && - test "$hardcode_direct_absolute" = no; then - add="$dir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$absdir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - func_append add_dir " -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - *) lib_linked=no ;; - esac - - if test "$lib_linked" != yes; then - func_fatal_configuration "unsupported hardcode properties" - fi - - if test -n "$add_shlibpath"; then - case :$compile_shlibpath: in - *":$add_shlibpath:"*) ;; - *) func_append compile_shlibpath "$add_shlibpath:" ;; - esac - fi - if test "$linkmode" = prog; then - test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" - test -n "$add" && compile_deplibs="$add $compile_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - if test "$hardcode_direct" != yes && - test "$hardcode_minus_L" != yes && - test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) func_append finalize_shlibpath "$libdir:" ;; - esac - fi - fi - fi - - if test "$linkmode" = prog || test "$opt_mode" = relink; then - add_shlibpath= - add_dir= - add= - # Finalize command for both is simple: just hardcode it. - if test "$hardcode_direct" = yes && - test "$hardcode_direct_absolute" = no; then - add="$libdir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$libdir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) func_append finalize_shlibpath "$libdir:" ;; - esac - add="-l$name" - elif test "$hardcode_automatic" = yes; then - if test -n "$inst_prefix_dir" && - test -f "$inst_prefix_dir$libdir/$linklib" ; then - add="$inst_prefix_dir$libdir/$linklib" - else - add="$libdir/$linklib" - fi - else - # We cannot seem to hardcode it, guess we'll fake it. - add_dir="-L$libdir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - func_append add_dir " -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - fi - - if test "$linkmode" = prog; then - test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" - test -n "$add" && finalize_deplibs="$add $finalize_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - fi - fi - elif test "$linkmode" = prog; then - # Here we assume that one of hardcode_direct or hardcode_minus_L - # is not unsupported. This is valid on all known static and - # shared platforms. - if test "$hardcode_direct" != unsupported; then - test -n "$old_library" && linklib="$old_library" - compile_deplibs="$dir/$linklib $compile_deplibs" - finalize_deplibs="$dir/$linklib $finalize_deplibs" - else - compile_deplibs="-l$name -L$dir $compile_deplibs" - finalize_deplibs="-l$name -L$dir $finalize_deplibs" - fi - elif test "$build_libtool_libs" = yes; then - # Not a shared library - if test "$deplibs_check_method" != pass_all; then - # We're trying link a shared library against a static one - # but the system doesn't support it. - - # Just print a warning and add the library to dependency_libs so - # that the program can be linked against the static library. - echo - $ECHO "*** Warning: This system can not link to static lib archive $lib." - echo "*** I have the capability to make that library automatically link in when" - echo "*** you link to this library. But I can only do this if you have a" - echo "*** shared version of the library, which you do not appear to have." - if test "$module" = yes; then - echo "*** But as you try to build a module library, libtool will still create " - echo "*** a static module, that should work as long as the dlopening application" - echo "*** is linked with the -dlopen flag to resolve symbols at runtime." - if test -z "$global_symbol_pipe"; then - echo - echo "*** However, this would only work if libtool was able to extract symbol" - echo "*** lists from a program, using \`nm' or equivalent, but libtool could" - echo "*** not find such a program. So, this module is probably useless." - echo "*** \`nm' from GNU binutils and a full rebuild may help." - fi - if test "$build_old_libs" = no; then - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - fi - else - deplibs="$dir/$old_library $deplibs" - link_static=yes - fi - fi # link shared/static library? - - if test "$linkmode" = lib; then - if test -n "$dependency_libs" && - { test "$hardcode_into_libs" != yes || - test "$build_old_libs" = yes || - test "$link_static" = yes; }; then - # Extract -R from dependency_libs - temp_deplibs= - for libdir in $dependency_libs; do - case $libdir in - -R*) func_stripname '-R' '' "$libdir" - temp_xrpath=$func_stripname_result - case " $xrpath " in - *" $temp_xrpath "*) ;; - *) func_append xrpath " $temp_xrpath";; - esac;; - *) func_append temp_deplibs " $libdir";; - esac - done - dependency_libs="$temp_deplibs" - fi - - func_append newlib_search_path " $absdir" - # Link against this library - test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" - # ... and its dependency_libs - tmp_libs= - for deplib in $dependency_libs; do - newdependency_libs="$deplib $newdependency_libs" - case $deplib in - -L*) func_stripname '-L' '' "$deplib" - func_resolve_sysroot "$func_stripname_result";; - *) func_resolve_sysroot "$deplib" ;; - esac - if $opt_preserve_dup_deps ; then - case "$tmp_libs " in - *" $func_resolve_sysroot_result "*) - func_append specialdeplibs " $func_resolve_sysroot_result" ;; - esac - fi - func_append tmp_libs " $func_resolve_sysroot_result" - done - - if test "$link_all_deplibs" != no; then - # Add the search paths of all dependency libraries - for deplib in $dependency_libs; do - path= - case $deplib in - -L*) path="$deplib" ;; - *.la) - func_resolve_sysroot "$deplib" - deplib=$func_resolve_sysroot_result - func_dirname "$deplib" "" "." - dir=$func_dirname_result - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; - *) - absdir=`cd "$dir" && pwd` - if test -z "$absdir"; then - func_warning "cannot determine absolute directory name of \`$dir'" - absdir="$dir" - fi - ;; - esac - if $GREP "^installed=no" $deplib > /dev/null; then - case $host in - *-*-darwin*) - depdepl= - eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` - if test -n "$deplibrary_names" ; then - for tmp in $deplibrary_names ; do - depdepl=$tmp - done - if test -f "$absdir/$objdir/$depdepl" ; then - depdepl="$absdir/$objdir/$depdepl" - darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` - if test -z "$darwin_install_name"; then - darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` - fi - func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" - func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" - path= - fi - fi - ;; - *) - path="-L$absdir/$objdir" - ;; - esac - else - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` - test -z "$libdir" && \ - func_fatal_error "\`$deplib' is not a valid libtool archive" - test "$absdir" != "$libdir" && \ - func_warning "\`$deplib' seems to be moved" - - path="-L$absdir" - fi - ;; - esac - case " $deplibs " in - *" $path "*) ;; - *) deplibs="$path $deplibs" ;; - esac - done - fi # link_all_deplibs != no - fi # linkmode = lib - done # for deplib in $libs - if test "$pass" = link; then - if test "$linkmode" = "prog"; then - compile_deplibs="$new_inherited_linker_flags $compile_deplibs" - finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" - else - compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - fi - fi - dependency_libs="$newdependency_libs" - if test "$pass" = dlpreopen; then - # Link the dlpreopened libraries before other libraries - for deplib in $save_deplibs; do - deplibs="$deplib $deplibs" - done - fi - if test "$pass" != dlopen; then - if test "$pass" != conv; then - # Make sure lib_search_path contains only unique directories. - lib_search_path= - for dir in $newlib_search_path; do - case "$lib_search_path " in - *" $dir "*) ;; - *) func_append lib_search_path " $dir" ;; - esac - done - newlib_search_path= - fi - - if test "$linkmode,$pass" != "prog,link"; then - vars="deplibs" - else - vars="compile_deplibs finalize_deplibs" - fi - for var in $vars dependency_libs; do - # Add libraries to $var in reverse order - eval tmp_libs=\"\$$var\" - new_libs= - for deplib in $tmp_libs; do - # FIXME: Pedantically, this is the right thing to do, so - # that some nasty dependency loop isn't accidentally - # broken: - #new_libs="$deplib $new_libs" - # Pragmatically, this seems to cause very few problems in - # practice: - case $deplib in - -L*) new_libs="$deplib $new_libs" ;; - -R*) ;; - *) - # And here is the reason: when a library appears more - # than once as an explicit dependence of a library, or - # is implicitly linked in more than once by the - # compiler, it is considered special, and multiple - # occurrences thereof are not removed. Compare this - # with having the same library being listed as a - # dependency of multiple other libraries: in this case, - # we know (pedantically, we assume) the library does not - # need to be listed more than once, so we keep only the - # last copy. This is not always right, but it is rare - # enough that we require users that really mean to play - # such unportable linking tricks to link the library - # using -Wl,-lname, so that libtool does not consider it - # for duplicate removal. - case " $specialdeplibs " in - *" $deplib "*) new_libs="$deplib $new_libs" ;; - *) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$deplib $new_libs" ;; - esac - ;; - esac - ;; - esac - done - tmp_libs= - for deplib in $new_libs; do - case $deplib in - -L*) - case " $tmp_libs " in - *" $deplib "*) ;; - *) func_append tmp_libs " $deplib" ;; - esac - ;; - *) func_append tmp_libs " $deplib" ;; - esac - done - eval $var=\"$tmp_libs\" - done # for var - fi - # Last step: remove runtime libs from dependency_libs - # (they stay in deplibs) - tmp_libs= - for i in $dependency_libs ; do - case " $predeps $postdeps $compiler_lib_search_path " in - *" $i "*) - i="" - ;; - esac - if test -n "$i" ; then - func_append tmp_libs " $i" - fi - done - dependency_libs=$tmp_libs - done # for pass - if test "$linkmode" = prog; then - dlfiles="$newdlfiles" - fi - if test "$linkmode" = prog || test "$linkmode" = lib; then - dlprefiles="$newdlprefiles" - fi - - case $linkmode in - oldlib) - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - func_warning "\`-dlopen' is ignored for archives" - fi - - case " $deplibs" in - *\ -l* | *\ -L*) - func_warning "\`-l' and \`-L' are ignored for archives" ;; - esac - - test -n "$rpath" && \ - func_warning "\`-rpath' is ignored for archives" - - test -n "$xrpath" && \ - func_warning "\`-R' is ignored for archives" - - test -n "$vinfo" && \ - func_warning "\`-version-info/-version-number' is ignored for archives" - - test -n "$release" && \ - func_warning "\`-release' is ignored for archives" - - test -n "$export_symbols$export_symbols_regex" && \ - func_warning "\`-export-symbols' is ignored for archives" - - # Now set the variables for building old libraries. - build_libtool_libs=no - oldlibs="$output" - func_append objs "$old_deplibs" - ;; - - lib) - # Make sure we only generate libraries of the form `libNAME.la'. - case $outputname in - lib*) - func_stripname 'lib' '.la' "$outputname" - name=$func_stripname_result - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - ;; - *) - test "$module" = no && \ - func_fatal_help "libtool library \`$output' must begin with \`lib'" - - if test "$need_lib_prefix" != no; then - # Add the "lib" prefix for modules if required - func_stripname '' '.la' "$outputname" - name=$func_stripname_result - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - else - func_stripname '' '.la' "$outputname" - libname=$func_stripname_result - fi - ;; - esac - - if test -n "$objs"; then - if test "$deplibs_check_method" != pass_all; then - func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" - else - echo - $ECHO "*** Warning: Linking the shared library $output against the non-libtool" - $ECHO "*** objects $objs is not portable!" - func_append libobjs " $objs" - fi - fi - - test "$dlself" != no && \ - func_warning "\`-dlopen self' is ignored for libtool libraries" - - set dummy $rpath - shift - test "$#" -gt 1 && \ - func_warning "ignoring multiple \`-rpath's for a libtool library" - - install_libdir="$1" - - oldlibs= - if test -z "$rpath"; then - if test "$build_libtool_libs" = yes; then - # Building a libtool convenience library. - # Some compilers have problems with a `.al' extension so - # convenience libraries should have the same extension an - # archive normally would. - oldlibs="$output_objdir/$libname.$libext $oldlibs" - build_libtool_libs=convenience - build_old_libs=yes - fi - - test -n "$vinfo" && \ - func_warning "\`-version-info/-version-number' is ignored for convenience libraries" - - test -n "$release" && \ - func_warning "\`-release' is ignored for convenience libraries" - else - - # Parse the version information argument. - save_ifs="$IFS"; IFS=':' - set dummy $vinfo 0 0 0 - shift - IFS="$save_ifs" - - test -n "$7" && \ - func_fatal_help "too many parameters to \`-version-info'" - - # convert absolute version numbers to libtool ages - # this retains compatibility with .la files and attempts - # to make the code below a bit more comprehensible - - case $vinfo_number in - yes) - number_major="$1" - number_minor="$2" - number_revision="$3" - # - # There are really only two kinds -- those that - # use the current revision as the major version - # and those that subtract age and use age as - # a minor version. But, then there is irix - # which has an extra 1 added just for fun - # - case $version_type in - # correct linux to gnu/linux during the next big refactor - darwin|linux|osf|windows|none) - func_arith $number_major + $number_minor - current=$func_arith_result - age="$number_minor" - revision="$number_revision" - ;; - freebsd-aout|freebsd-elf|qnx|sunos) - current="$number_major" - revision="$number_minor" - age="0" - ;; - irix|nonstopux) - func_arith $number_major + $number_minor - current=$func_arith_result - age="$number_minor" - revision="$number_minor" - lt_irix_increment=no - ;; - esac - ;; - no) - current="$1" - revision="$2" - age="$3" - ;; - esac - - # Check that each of the things are valid numbers. - case $current in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - func_error "CURRENT \`$current' must be a nonnegative integer" - func_fatal_error "\`$vinfo' is not valid version information" - ;; - esac - - case $revision in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - func_error "REVISION \`$revision' must be a nonnegative integer" - func_fatal_error "\`$vinfo' is not valid version information" - ;; - esac - - case $age in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - func_error "AGE \`$age' must be a nonnegative integer" - func_fatal_error "\`$vinfo' is not valid version information" - ;; - esac - - if test "$age" -gt "$current"; then - func_error "AGE \`$age' is greater than the current interface number \`$current'" - func_fatal_error "\`$vinfo' is not valid version information" - fi - - # Calculate the version variables. - major= - versuffix= - verstring= - case $version_type in - none) ;; - - darwin) - # Like Linux, but with the current version available in - # verstring for coding it into the library header - func_arith $current - $age - major=.$func_arith_result - versuffix="$major.$age.$revision" - # Darwin ld doesn't like 0 for these options... - func_arith $current + 1 - minor_current=$func_arith_result - xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" - verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" - ;; - - freebsd-aout) - major=".$current" - versuffix=".$current.$revision"; - ;; - - freebsd-elf) - major=".$current" - versuffix=".$current" - ;; - - irix | nonstopux) - if test "X$lt_irix_increment" = "Xno"; then - func_arith $current - $age - else - func_arith $current - $age + 1 - fi - major=$func_arith_result - - case $version_type in - nonstopux) verstring_prefix=nonstopux ;; - *) verstring_prefix=sgi ;; - esac - verstring="$verstring_prefix$major.$revision" - - # Add in all the interfaces that we are compatible with. - loop=$revision - while test "$loop" -ne 0; do - func_arith $revision - $loop - iface=$func_arith_result - func_arith $loop - 1 - loop=$func_arith_result - verstring="$verstring_prefix$major.$iface:$verstring" - done - - # Before this point, $major must not contain `.'. - major=.$major - versuffix="$major.$revision" - ;; - - linux) # correct to gnu/linux during the next big refactor - func_arith $current - $age - major=.$func_arith_result - versuffix="$major.$age.$revision" - ;; - - osf) - func_arith $current - $age - major=.$func_arith_result - versuffix=".$current.$age.$revision" - verstring="$current.$age.$revision" - - # Add in all the interfaces that we are compatible with. - loop=$age - while test "$loop" -ne 0; do - func_arith $current - $loop - iface=$func_arith_result - func_arith $loop - 1 - loop=$func_arith_result - verstring="$verstring:${iface}.0" - done - - # Make executables depend on our current version. - func_append verstring ":${current}.0" - ;; - - qnx) - major=".$current" - versuffix=".$current" - ;; - - sunos) - major=".$current" - versuffix=".$current.$revision" - ;; - - windows) - # Use '-' rather than '.', since we only want one - # extension on DOS 8.3 filesystems. - func_arith $current - $age - major=$func_arith_result - versuffix="-$major" - ;; - - *) - func_fatal_configuration "unknown library version type \`$version_type'" - ;; - esac - - # Clear the version info if we defaulted, and they specified a release. - if test -z "$vinfo" && test -n "$release"; then - major= - case $version_type in - darwin) - # we can't check for "0.0" in archive_cmds due to quoting - # problems, so we reset it completely - verstring= - ;; - *) - verstring="0.0" - ;; - esac - if test "$need_version" = no; then - versuffix= - else - versuffix=".0.0" - fi - fi - - # Remove version info from name if versioning should be avoided - if test "$avoid_version" = yes && test "$need_version" = no; then - major= - versuffix= - verstring="" - fi - - # Check to see if the archive will have undefined symbols. - if test "$allow_undefined" = yes; then - if test "$allow_undefined_flag" = unsupported; then - func_warning "undefined symbols not allowed in $host shared libraries" - build_libtool_libs=no - build_old_libs=yes - fi - else - # Don't allow undefined symbols. - allow_undefined_flag="$no_undefined_flag" - fi - - fi - - func_generate_dlsyms "$libname" "$libname" "yes" - func_append libobjs " $symfileobj" - test "X$libobjs" = "X " && libobjs= - - if test "$opt_mode" != relink; then - # Remove our outputs, but don't remove object files since they - # may have been created when compiling PIC objects. - removelist= - tempremovelist=`$ECHO "$output_objdir/*"` - for p in $tempremovelist; do - case $p in - *.$objext | *.gcno) - ;; - $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) - if test "X$precious_files_regex" != "X"; then - if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 - then - continue - fi - fi - func_append removelist " $p" - ;; - *) ;; - esac - done - test -n "$removelist" && \ - func_show_eval "${RM}r \$removelist" - fi - - # Now set the variables for building old libraries. - if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then - func_append oldlibs " $output_objdir/$libname.$libext" - - # Transform .lo files to .o files. - oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` - fi - - # Eliminate all temporary directories. - #for path in $notinst_path; do - # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` - # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` - # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` - #done - - if test -n "$xrpath"; then - # If the user specified any rpath flags, then add them. - temp_xrpath= - for libdir in $xrpath; do - func_replace_sysroot "$libdir" - func_append temp_xrpath " -R$func_replace_sysroot_result" - case "$finalize_rpath " in - *" $libdir "*) ;; - *) func_append finalize_rpath " $libdir" ;; - esac - done - if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then - dependency_libs="$temp_xrpath $dependency_libs" - fi - fi - - # Make sure dlfiles contains only unique files that won't be dlpreopened - old_dlfiles="$dlfiles" - dlfiles= - for lib in $old_dlfiles; do - case " $dlprefiles $dlfiles " in - *" $lib "*) ;; - *) func_append dlfiles " $lib" ;; - esac - done - - # Make sure dlprefiles contains only unique files - old_dlprefiles="$dlprefiles" - dlprefiles= - for lib in $old_dlprefiles; do - case "$dlprefiles " in - *" $lib "*) ;; - *) func_append dlprefiles " $lib" ;; - esac - done - - if test "$build_libtool_libs" = yes; then - if test -n "$rpath"; then - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) - # these systems don't actually have a c library (as such)! - ;; - *-*-rhapsody* | *-*-darwin1.[012]) - # Rhapsody C library is in the System framework - func_append deplibs " System.ltframework" - ;; - *-*-netbsd*) - # Don't link with libc until the a.out ld.so is fixed. - ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc due to us having libc/libc_r. - ;; - *-*-sco3.2v5* | *-*-sco5v6*) - # Causes problems with __ctype - ;; - *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) - # Compiler inserts libc in the correct place for threads to work - ;; - *) - # Add libc to deplibs on all other systems if necessary. - if test "$build_libtool_need_lc" = "yes"; then - func_append deplibs " -lc" - fi - ;; - esac - fi - - # Transform deplibs into only deplibs that can be linked in shared. - name_save=$name - libname_save=$libname - release_save=$release - versuffix_save=$versuffix - major_save=$major - # I'm not sure if I'm treating the release correctly. I think - # release should show up in the -l (ie -lgmp5) so we don't want to - # add it in twice. Is that correct? - release="" - versuffix="" - major="" - newdeplibs= - droppeddeps=no - case $deplibs_check_method in - pass_all) - # Don't check for shared/static. Everything works. - # This might be a little naive. We might want to check - # whether the library exists or not. But this is on - # osf3 & osf4 and I'm not really sure... Just - # implementing what was already the behavior. - newdeplibs=$deplibs - ;; - test_compile) - # This code stresses the "libraries are programs" paradigm to its - # limits. Maybe even breaks it. We compile a program, linking it - # against the deplibs as a proxy for the library. Then we can check - # whether they linked in statically or dynamically with ldd. - $opt_dry_run || $RM conftest.c - cat > conftest.c </dev/null` - $nocaseglob - else - potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` - fi - for potent_lib in $potential_libs; do - # Follow soft links. - if ls -lLd "$potent_lib" 2>/dev/null | - $GREP " -> " >/dev/null; then - continue - fi - # The statement above tries to avoid entering an - # endless loop below, in case of cyclic links. - # We might still enter an endless loop, since a link - # loop can be closed while we follow links, - # but so what? - potlib="$potent_lib" - while test -h "$potlib" 2>/dev/null; do - potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` - case $potliblink in - [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; - *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; - esac - done - if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | - $SED -e 10q | - $EGREP "$file_magic_regex" > /dev/null; then - func_append newdeplibs " $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - echo - $ECHO "*** Warning: linker path does not have real file for library $a_deplib." - echo "*** I have the capability to make that library automatically link in when" - echo "*** you link to this library. But I can only do this if you have a" - echo "*** shared version of the library, which you do not appear to have" - echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $ECHO "*** with $libname but no candidates were found. (...for file magic test)" - else - $ECHO "*** with $libname and none of the candidates passed a file format test" - $ECHO "*** using a file magic. Last file checked: $potlib" - fi - fi - ;; - *) - # Add a -L argument. - func_append newdeplibs " $a_deplib" - ;; - esac - done # Gone through all deplibs. - ;; - match_pattern*) - set dummy $deplibs_check_method; shift - match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` - for a_deplib in $deplibs; do - case $a_deplib in - -l*) - func_stripname -l '' "$a_deplib" - name=$func_stripname_result - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $a_deplib "*) - func_append newdeplibs " $a_deplib" - a_deplib="" - ;; - esac - fi - if test -n "$a_deplib" ; then - libname=`eval "\\$ECHO \"$libname_spec\""` - for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do - potential_libs=`ls $i/$libname[.-]* 2>/dev/null` - for potent_lib in $potential_libs; do - potlib="$potent_lib" # see symlink-check above in file_magic test - if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ - $EGREP "$match_pattern_regex" > /dev/null; then - func_append newdeplibs " $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - echo - $ECHO "*** Warning: linker path does not have real file for library $a_deplib." - echo "*** I have the capability to make that library automatically link in when" - echo "*** you link to this library. But I can only do this if you have a" - echo "*** shared version of the library, which you do not appear to have" - echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" - else - $ECHO "*** with $libname and none of the candidates passed a file format test" - $ECHO "*** using a regex pattern. Last file checked: $potlib" - fi - fi - ;; - *) - # Add a -L argument. - func_append newdeplibs " $a_deplib" - ;; - esac - done # Gone through all deplibs. - ;; - none | unknown | *) - newdeplibs="" - tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - for i in $predeps $postdeps ; do - # can't use Xsed below, because $i might contain '/' - tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` - done - fi - case $tmp_deplibs in - *[!\ \ ]*) - echo - if test "X$deplibs_check_method" = "Xnone"; then - echo "*** Warning: inter-library dependencies are not supported in this platform." - else - echo "*** Warning: inter-library dependencies are not known to be supported." - fi - echo "*** All declared inter-library dependencies are being dropped." - droppeddeps=yes - ;; - esac - ;; - esac - versuffix=$versuffix_save - major=$major_save - release=$release_save - libname=$libname_save - name=$name_save - - case $host in - *-*-rhapsody* | *-*-darwin1.[012]) - # On Rhapsody replace the C library with the System framework - newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` - ;; - esac - - if test "$droppeddeps" = yes; then - if test "$module" = yes; then - echo - echo "*** Warning: libtool could not satisfy all declared inter-library" - $ECHO "*** dependencies of module $libname. Therefore, libtool will create" - echo "*** a static module, that should work as long as the dlopening" - echo "*** application is linked with the -dlopen flag." - if test -z "$global_symbol_pipe"; then - echo - echo "*** However, this would only work if libtool was able to extract symbol" - echo "*** lists from a program, using \`nm' or equivalent, but libtool could" - echo "*** not find such a program. So, this module is probably useless." - echo "*** \`nm' from GNU binutils and a full rebuild may help." - fi - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - else - echo "*** The inter-library dependencies that have been dropped here will be" - echo "*** automatically added whenever a program is linked with this library" - echo "*** or is declared to -dlopen it." - - if test "$allow_undefined" = no; then - echo - echo "*** Since this library must not contain undefined symbols," - echo "*** because either the platform does not support them or" - echo "*** it was explicitly requested with -no-undefined," - echo "*** libtool will only create a static version of it." - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - fi - fi - fi - # Done checking deplibs! - deplibs=$newdeplibs - fi - # Time to change all our "foo.ltframework" stuff back to "-framework foo" - case $host in - *-*-darwin*) - newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - ;; - esac - - # move library search paths that coincide with paths to not yet - # installed libraries to the beginning of the library search list - new_libs= - for path in $notinst_path; do - case " $new_libs " in - *" -L$path/$objdir "*) ;; - *) - case " $deplibs " in - *" -L$path/$objdir "*) - func_append new_libs " -L$path/$objdir" ;; - esac - ;; - esac - done - for deplib in $deplibs; do - case $deplib in - -L*) - case " $new_libs " in - *" $deplib "*) ;; - *) func_append new_libs " $deplib" ;; - esac - ;; - *) func_append new_libs " $deplib" ;; - esac - done - deplibs="$new_libs" - - # All the library-specific variables (install_libdir is set above). - library_names= - old_library= - dlname= - - # Test again, we may have decided not to build it any more - if test "$build_libtool_libs" = yes; then - # Remove ${wl} instances when linking with ld. - # FIXME: should test the right _cmds variable. - case $archive_cmds in - *\$LD\ *) wl= ;; - esac - if test "$hardcode_into_libs" = yes; then - # Hardcode the library paths - hardcode_libdirs= - dep_rpath= - rpath="$finalize_rpath" - test "$opt_mode" != relink && rpath="$compile_rpath$rpath" - for libdir in $rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - func_replace_sysroot "$libdir" - libdir=$func_replace_sysroot_result - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - func_append dep_rpath " $flag" - fi - elif test -n "$runpath_var"; then - case "$perm_rpath " in - *" $libdir "*) ;; - *) func_append perm_rpath " $libdir" ;; - esac - fi - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" - fi - if test -n "$runpath_var" && test -n "$perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $perm_rpath; do - func_append rpath "$dir:" - done - eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" - fi - test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" - fi - - shlibpath="$finalize_shlibpath" - test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" - if test -n "$shlibpath"; then - eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" - fi - - # Get the real and link names of the library. - eval shared_ext=\"$shrext_cmds\" - eval library_names=\"$library_names_spec\" - set dummy $library_names - shift - realname="$1" - shift - - if test -n "$soname_spec"; then - eval soname=\"$soname_spec\" - else - soname="$realname" - fi - if test -z "$dlname"; then - dlname=$soname - fi - - lib="$output_objdir/$realname" - linknames= - for link - do - func_append linknames " $link" - done - - # Use standard objects if they are pic - test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` - test "X$libobjs" = "X " && libobjs= - - delfiles= - if test -n "$export_symbols" && test -n "$include_expsyms"; then - $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" - export_symbols="$output_objdir/$libname.uexp" - func_append delfiles " $export_symbols" - fi - - orig_export_symbols= - case $host_os in - cygwin* | mingw* | cegcc*) - if test -n "$export_symbols" && test -z "$export_symbols_regex"; then - # exporting using user supplied symfile - if test "x`$SED 1q $export_symbols`" != xEXPORTS; then - # and it's NOT already a .def file. Must figure out - # which of the given symbols are data symbols and tag - # them as such. So, trigger use of export_symbols_cmds. - # export_symbols gets reassigned inside the "prepare - # the list of exported symbols" if statement, so the - # include_expsyms logic still works. - orig_export_symbols="$export_symbols" - export_symbols= - always_export_symbols=yes - fi - fi - ;; - esac - - # Prepare the list of exported symbols - if test -z "$export_symbols"; then - if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then - func_verbose "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" - $opt_dry_run || $RM $export_symbols - cmds=$export_symbols_cmds - save_ifs="$IFS"; IFS='~' - for cmd1 in $cmds; do - IFS="$save_ifs" - # Take the normal branch if the nm_file_list_spec branch - # doesn't work or if tool conversion is not needed. - case $nm_file_list_spec~$to_tool_file_cmd in - *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) - try_normal_branch=yes - eval cmd=\"$cmd1\" - func_len " $cmd" - len=$func_len_result - ;; - *) - try_normal_branch=no - ;; - esac - if test "$try_normal_branch" = yes \ - && { test "$len" -lt "$max_cmd_len" \ - || test "$max_cmd_len" -le -1; } - then - func_show_eval "$cmd" 'exit $?' - skipped_export=false - elif test -n "$nm_file_list_spec"; then - func_basename "$output" - output_la=$func_basename_result - save_libobjs=$libobjs - save_output=$output - output=${output_objdir}/${output_la}.nm - func_to_tool_file "$output" - libobjs=$nm_file_list_spec$func_to_tool_file_result - func_append delfiles " $output" - func_verbose "creating $NM input file list: $output" - for obj in $save_libobjs; do - func_to_tool_file "$obj" - $ECHO "$func_to_tool_file_result" - done > "$output" - eval cmd=\"$cmd1\" - func_show_eval "$cmd" 'exit $?' - output=$save_output - libobjs=$save_libobjs - skipped_export=false - else - # The command line is too long to execute in one step. - func_verbose "using reloadable object file for export list..." - skipped_export=: - # Break out early, otherwise skipped_export may be - # set to false by a later but shorter cmd. - break - fi - done - IFS="$save_ifs" - if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then - func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' - func_show_eval '$MV "${export_symbols}T" "$export_symbols"' - fi - fi - fi - - if test -n "$export_symbols" && test -n "$include_expsyms"; then - tmp_export_symbols="$export_symbols" - test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" - $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' - fi - - if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then - # The given exports_symbols file has to be filtered, so filter it. - func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" - # FIXME: $output_objdir/$libname.filter potentially contains lots of - # 's' commands which not all seds can handle. GNU sed should be fine - # though. Also, the filter scales superlinearly with the number of - # global variables. join(1) would be nice here, but unfortunately - # isn't a blessed tool. - $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter - func_append delfiles " $export_symbols $output_objdir/$libname.filter" - export_symbols=$output_objdir/$libname.def - $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols - fi - - tmp_deplibs= - for test_deplib in $deplibs; do - case " $convenience " in - *" $test_deplib "*) ;; - *) - func_append tmp_deplibs " $test_deplib" - ;; - esac - done - deplibs="$tmp_deplibs" - - if test -n "$convenience"; then - if test -n "$whole_archive_flag_spec" && - test "$compiler_needs_object" = yes && - test -z "$libobjs"; then - # extract the archives, so we have objects to list. - # TODO: could optimize this to just extract one archive. - whole_archive_flag_spec= - fi - if test -n "$whole_archive_flag_spec"; then - save_libobjs=$libobjs - eval libobjs=\"\$libobjs $whole_archive_flag_spec\" - test "X$libobjs" = "X " && libobjs= - else - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - - func_extract_archives $gentop $convenience - func_append libobjs " $func_extract_archives_result" - test "X$libobjs" = "X " && libobjs= - fi - fi - - if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then - eval flag=\"$thread_safe_flag_spec\" - func_append linker_flags " $flag" - fi - - # Make a backup of the uninstalled library when relinking - if test "$opt_mode" = relink; then - $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? - fi - - # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then - if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then - eval test_cmds=\"$module_expsym_cmds\" - cmds=$module_expsym_cmds - else - eval test_cmds=\"$module_cmds\" - cmds=$module_cmds - fi - else - if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then - eval test_cmds=\"$archive_expsym_cmds\" - cmds=$archive_expsym_cmds - else - eval test_cmds=\"$archive_cmds\" - cmds=$archive_cmds - fi - fi - - if test "X$skipped_export" != "X:" && - func_len " $test_cmds" && - len=$func_len_result && - test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then - : - else - # The command line is too long to link in one step, link piecewise - # or, if using GNU ld and skipped_export is not :, use a linker - # script. - - # Save the value of $output and $libobjs because we want to - # use them later. If we have whole_archive_flag_spec, we - # want to use save_libobjs as it was before - # whole_archive_flag_spec was expanded, because we can't - # assume the linker understands whole_archive_flag_spec. - # This may have to be revisited, in case too many - # convenience libraries get linked in and end up exceeding - # the spec. - if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then - save_libobjs=$libobjs - fi - save_output=$output - func_basename "$output" - output_la=$func_basename_result - - # Clear the reloadable object creation command queue and - # initialize k to one. - test_cmds= - concat_cmds= - objlist= - last_robj= - k=1 - - if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then - output=${output_objdir}/${output_la}.lnkscript - func_verbose "creating GNU ld script: $output" - echo 'INPUT (' > $output - for obj in $save_libobjs - do - func_to_tool_file "$obj" - $ECHO "$func_to_tool_file_result" >> $output - done - echo ')' >> $output - func_append delfiles " $output" - func_to_tool_file "$output" - output=$func_to_tool_file_result - elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then - output=${output_objdir}/${output_la}.lnk - func_verbose "creating linker input file list: $output" - : > $output - set x $save_libobjs - shift - firstobj= - if test "$compiler_needs_object" = yes; then - firstobj="$1 " - shift - fi - for obj - do - func_to_tool_file "$obj" - $ECHO "$func_to_tool_file_result" >> $output - done - func_append delfiles " $output" - func_to_tool_file "$output" - output=$firstobj\"$file_list_spec$func_to_tool_file_result\" - else - if test -n "$save_libobjs"; then - func_verbose "creating reloadable object files..." - output=$output_objdir/$output_la-${k}.$objext - eval test_cmds=\"$reload_cmds\" - func_len " $test_cmds" - len0=$func_len_result - len=$len0 - - # Loop over the list of objects to be linked. - for obj in $save_libobjs - do - func_len " $obj" - func_arith $len + $func_len_result - len=$func_arith_result - if test "X$objlist" = X || - test "$len" -lt "$max_cmd_len"; then - func_append objlist " $obj" - else - # The command $test_cmds is almost too long, add a - # command to the queue. - if test "$k" -eq 1 ; then - # The first file doesn't have a previous command to add. - reload_objs=$objlist - eval concat_cmds=\"$reload_cmds\" - else - # All subsequent reloadable object files will link in - # the last one created. - reload_objs="$objlist $last_robj" - eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" - fi - last_robj=$output_objdir/$output_la-${k}.$objext - func_arith $k + 1 - k=$func_arith_result - output=$output_objdir/$output_la-${k}.$objext - objlist=" $obj" - func_len " $last_robj" - func_arith $len0 + $func_len_result - len=$func_arith_result - fi - done - # Handle the remaining objects by creating one last - # reloadable object file. All subsequent reloadable object - # files will link in the last one created. - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - reload_objs="$objlist $last_robj" - eval concat_cmds=\"\${concat_cmds}$reload_cmds\" - if test -n "$last_robj"; then - eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" - fi - func_append delfiles " $output" - - else - output= - fi - - if ${skipped_export-false}; then - func_verbose "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" - $opt_dry_run || $RM $export_symbols - libobjs=$output - # Append the command to create the export file. - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" - if test -n "$last_robj"; then - eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" - fi - fi - - test -n "$save_libobjs" && - func_verbose "creating a temporary reloadable object file: $output" - - # Loop through the commands generated above and execute them. - save_ifs="$IFS"; IFS='~' - for cmd in $concat_cmds; do - IFS="$save_ifs" - $opt_silent || { - func_quote_for_expand "$cmd" - eval "func_echo $func_quote_for_expand_result" - } - $opt_dry_run || eval "$cmd" || { - lt_exit=$? - - # Restore the uninstalled library and exit - if test "$opt_mode" = relink; then - ( cd "$output_objdir" && \ - $RM "${realname}T" && \ - $MV "${realname}U" "$realname" ) - fi - - exit $lt_exit - } - done - IFS="$save_ifs" - - if test -n "$export_symbols_regex" && ${skipped_export-false}; then - func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' - func_show_eval '$MV "${export_symbols}T" "$export_symbols"' - fi - fi - - if ${skipped_export-false}; then - if test -n "$export_symbols" && test -n "$include_expsyms"; then - tmp_export_symbols="$export_symbols" - test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" - $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' - fi - - if test -n "$orig_export_symbols"; then - # The given exports_symbols file has to be filtered, so filter it. - func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" - # FIXME: $output_objdir/$libname.filter potentially contains lots of - # 's' commands which not all seds can handle. GNU sed should be fine - # though. Also, the filter scales superlinearly with the number of - # global variables. join(1) would be nice here, but unfortunately - # isn't a blessed tool. - $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter - func_append delfiles " $export_symbols $output_objdir/$libname.filter" - export_symbols=$output_objdir/$libname.def - $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols - fi - fi - - libobjs=$output - # Restore the value of output. - output=$save_output - - if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then - eval libobjs=\"\$libobjs $whole_archive_flag_spec\" - test "X$libobjs" = "X " && libobjs= - fi - # Expand the library linking commands again to reset the - # value of $libobjs for piecewise linking. - - # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then - if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then - cmds=$module_expsym_cmds - else - cmds=$module_cmds - fi - else - if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then - cmds=$archive_expsym_cmds - else - cmds=$archive_cmds - fi - fi - fi - - if test -n "$delfiles"; then - # Append the command to remove temporary files to $cmds. - eval cmds=\"\$cmds~\$RM $delfiles\" - fi - - # Add any objects from preloaded convenience libraries - if test -n "$dlprefiles"; then - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - - func_extract_archives $gentop $dlprefiles - func_append libobjs " $func_extract_archives_result" - test "X$libobjs" = "X " && libobjs= - fi - - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $opt_silent || { - func_quote_for_expand "$cmd" - eval "func_echo $func_quote_for_expand_result" - } - $opt_dry_run || eval "$cmd" || { - lt_exit=$? - - # Restore the uninstalled library and exit - if test "$opt_mode" = relink; then - ( cd "$output_objdir" && \ - $RM "${realname}T" && \ - $MV "${realname}U" "$realname" ) - fi - - exit $lt_exit - } - done - IFS="$save_ifs" - - # Restore the uninstalled library and exit - if test "$opt_mode" = relink; then - $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? - - if test -n "$convenience"; then - if test -z "$whole_archive_flag_spec"; then - func_show_eval '${RM}r "$gentop"' - fi - fi - - exit $EXIT_SUCCESS - fi - - # Create links to the real library. - for linkname in $linknames; do - if test "$realname" != "$linkname"; then - func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' - fi - done - - # If -module or -export-dynamic was specified, set the dlname. - if test "$module" = yes || test "$export_dynamic" = yes; then - # On all known operating systems, these are identical. - dlname="$soname" - fi - fi - ;; - - obj) - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - func_warning "\`-dlopen' is ignored for objects" - fi - - case " $deplibs" in - *\ -l* | *\ -L*) - func_warning "\`-l' and \`-L' are ignored for objects" ;; - esac - - test -n "$rpath" && \ - func_warning "\`-rpath' is ignored for objects" - - test -n "$xrpath" && \ - func_warning "\`-R' is ignored for objects" - - test -n "$vinfo" && \ - func_warning "\`-version-info' is ignored for objects" - - test -n "$release" && \ - func_warning "\`-release' is ignored for objects" - - case $output in - *.lo) - test -n "$objs$old_deplibs" && \ - func_fatal_error "cannot build library object \`$output' from non-libtool objects" - - libobj=$output - func_lo2o "$libobj" - obj=$func_lo2o_result - ;; - *) - libobj= - obj="$output" - ;; - esac - - # Delete the old objects. - $opt_dry_run || $RM $obj $libobj - - # Objects from convenience libraries. This assumes - # single-version convenience libraries. Whenever we create - # different ones for PIC/non-PIC, this we'll have to duplicate - # the extraction. - reload_conv_objs= - gentop= - # reload_cmds runs $LD directly, so let us get rid of - # -Wl from whole_archive_flag_spec and hope we can get by with - # turning comma into space.. - wl= - - if test -n "$convenience"; then - if test -n "$whole_archive_flag_spec"; then - eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" - reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` - else - gentop="$output_objdir/${obj}x" - func_append generated " $gentop" - - func_extract_archives $gentop $convenience - reload_conv_objs="$reload_objs $func_extract_archives_result" - fi - fi - - # If we're not building shared, we need to use non_pic_objs - test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" - - # Create the old-style object. - reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test - - output="$obj" - func_execute_cmds "$reload_cmds" 'exit $?' - - # Exit if we aren't doing a library object file. - if test -z "$libobj"; then - if test -n "$gentop"; then - func_show_eval '${RM}r "$gentop"' - fi - - exit $EXIT_SUCCESS - fi - - if test "$build_libtool_libs" != yes; then - if test -n "$gentop"; then - func_show_eval '${RM}r "$gentop"' - fi - - # Create an invalid libtool object if no PIC, so that we don't - # accidentally link it into a program. - # $show "echo timestamp > $libobj" - # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? - exit $EXIT_SUCCESS - fi - - if test -n "$pic_flag" || test "$pic_mode" != default; then - # Only do commands if we really have different PIC objects. - reload_objs="$libobjs $reload_conv_objs" - output="$libobj" - func_execute_cmds "$reload_cmds" 'exit $?' - fi - - if test -n "$gentop"; then - func_show_eval '${RM}r "$gentop"' - fi - - exit $EXIT_SUCCESS - ;; - - prog) - case $host in - *cygwin*) func_stripname '' '.exe' "$output" - output=$func_stripname_result.exe;; - esac - test -n "$vinfo" && \ - func_warning "\`-version-info' is ignored for programs" - - test -n "$release" && \ - func_warning "\`-release' is ignored for programs" - - test "$preload" = yes \ - && test "$dlopen_support" = unknown \ - && test "$dlopen_self" = unknown \ - && test "$dlopen_self_static" = unknown && \ - func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." - - case $host in - *-*-rhapsody* | *-*-darwin1.[012]) - # On Rhapsody replace the C library is the System framework - compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` - finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` - ;; - esac - - case $host in - *-*-darwin*) - # Don't allow lazy linking, it breaks C++ global constructors - # But is supposedly fixed on 10.4 or later (yay!). - if test "$tagname" = CXX ; then - case ${MACOSX_DEPLOYMENT_TARGET-10.0} in - 10.[0123]) - func_append compile_command " ${wl}-bind_at_load" - func_append finalize_command " ${wl}-bind_at_load" - ;; - esac - fi - # Time to change all our "foo.ltframework" stuff back to "-framework foo" - compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - ;; - esac - - - # move library search paths that coincide with paths to not yet - # installed libraries to the beginning of the library search list - new_libs= - for path in $notinst_path; do - case " $new_libs " in - *" -L$path/$objdir "*) ;; - *) - case " $compile_deplibs " in - *" -L$path/$objdir "*) - func_append new_libs " -L$path/$objdir" ;; - esac - ;; - esac - done - for deplib in $compile_deplibs; do - case $deplib in - -L*) - case " $new_libs " in - *" $deplib "*) ;; - *) func_append new_libs " $deplib" ;; - esac - ;; - *) func_append new_libs " $deplib" ;; - esac - done - compile_deplibs="$new_libs" - - - func_append compile_command " $compile_deplibs" - func_append finalize_command " $finalize_deplibs" - - if test -n "$rpath$xrpath"; then - # If the user specified any rpath flags, then add them. - for libdir in $rpath $xrpath; do - # This is the magic to use -rpath. - case "$finalize_rpath " in - *" $libdir "*) ;; - *) func_append finalize_rpath " $libdir" ;; - esac - done - fi - - # Now hardcode the library paths - rpath= - hardcode_libdirs= - for libdir in $compile_rpath $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - func_append rpath " $flag" - fi - elif test -n "$runpath_var"; then - case "$perm_rpath " in - *" $libdir "*) ;; - *) func_append perm_rpath " $libdir" ;; - esac - fi - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) - testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$libdir:"*) ;; - ::) dllsearchpath=$libdir;; - *) func_append dllsearchpath ":$libdir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - ::) dllsearchpath=$testbindir;; - *) func_append dllsearchpath ":$testbindir";; - esac - ;; - esac - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - compile_rpath="$rpath" - - rpath= - hardcode_libdirs= - for libdir in $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - func_append rpath " $flag" - fi - elif test -n "$runpath_var"; then - case "$finalize_perm_rpath " in - *" $libdir "*) ;; - *) func_append finalize_perm_rpath " $libdir" ;; - esac - fi - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - finalize_rpath="$rpath" - - if test -n "$libobjs" && test "$build_old_libs" = yes; then - # Transform all the library objects into standard objects. - compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` - finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` - fi - - func_generate_dlsyms "$outputname" "@PROGRAM@" "no" - - # template prelinking step - if test -n "$prelink_cmds"; then - func_execute_cmds "$prelink_cmds" 'exit $?' - fi - - wrappers_required=yes - case $host in - *cegcc* | *mingw32ce*) - # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. - wrappers_required=no - ;; - *cygwin* | *mingw* ) - if test "$build_libtool_libs" != yes; then - wrappers_required=no - fi - ;; - *) - if test "$need_relink" = no || test "$build_libtool_libs" != yes; then - wrappers_required=no - fi - ;; - esac - if test "$wrappers_required" = no; then - # Replace the output file specification. - compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` - link_command="$compile_command$compile_rpath" - - # We have no uninstalled library dependencies, so finalize right now. - exit_status=0 - func_show_eval "$link_command" 'exit_status=$?' - - if test -n "$postlink_cmds"; then - func_to_tool_file "$output" - postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` - func_execute_cmds "$postlink_cmds" 'exit $?' - fi - - # Delete the generated files. - if test -f "$output_objdir/${outputname}S.${objext}"; then - func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' - fi - - exit $exit_status - fi - - if test -n "$compile_shlibpath$finalize_shlibpath"; then - compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" - fi - if test -n "$finalize_shlibpath"; then - finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" - fi - - compile_var= - finalize_var= - if test -n "$runpath_var"; then - if test -n "$perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $perm_rpath; do - func_append rpath "$dir:" - done - compile_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - if test -n "$finalize_perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $finalize_perm_rpath; do - func_append rpath "$dir:" - done - finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - fi - - if test "$no_install" = yes; then - # We don't need to create a wrapper script. - link_command="$compile_var$compile_command$compile_rpath" - # Replace the output file specification. - link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` - # Delete the old output file. - $opt_dry_run || $RM $output - # Link the executable and exit - func_show_eval "$link_command" 'exit $?' - - if test -n "$postlink_cmds"; then - func_to_tool_file "$output" - postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` - func_execute_cmds "$postlink_cmds" 'exit $?' - fi - - exit $EXIT_SUCCESS - fi - - if test "$hardcode_action" = relink; then - # Fast installation is not supported - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - - func_warning "this platform does not like uninstalled shared libraries" - func_warning "\`$output' will be relinked during installation" - else - if test "$fast_install" != no; then - link_command="$finalize_var$compile_command$finalize_rpath" - if test "$fast_install" = yes; then - relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` - else - # fast_install is set to needless - relink_command= - fi - else - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - fi - fi - - # Replace the output file specification. - link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` - - # Delete the old output files. - $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname - - func_show_eval "$link_command" 'exit $?' - - if test -n "$postlink_cmds"; then - func_to_tool_file "$output_objdir/$outputname" - postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` - func_execute_cmds "$postlink_cmds" 'exit $?' - fi - - # Now create the wrapper script. - func_verbose "creating $output" - - # Quote the relink command for shipping. - if test -n "$relink_command"; then - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - func_quote_for_eval "$var_value" - relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" - fi - done - relink_command="(cd `pwd`; $relink_command)" - relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` - fi - - # Only actually do things if not in dry run mode. - $opt_dry_run || { - # win32 will think the script is a binary if it has - # a .exe suffix, so we strip it off here. - case $output in - *.exe) func_stripname '' '.exe' "$output" - output=$func_stripname_result ;; - esac - # test for cygwin because mv fails w/o .exe extensions - case $host in - *cygwin*) - exeext=.exe - func_stripname '' '.exe' "$outputname" - outputname=$func_stripname_result ;; - *) exeext= ;; - esac - case $host in - *cygwin* | *mingw* ) - func_dirname_and_basename "$output" "" "." - output_name=$func_basename_result - output_path=$func_dirname_result - cwrappersource="$output_path/$objdir/lt-$output_name.c" - cwrapper="$output_path/$output_name.exe" - $RM $cwrappersource $cwrapper - trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 - - func_emit_cwrapperexe_src > $cwrappersource - - # The wrapper executable is built using the $host compiler, - # because it contains $host paths and files. If cross- - # compiling, it, like the target executable, must be - # executed on the $host or under an emulation environment. - $opt_dry_run || { - $LTCC $LTCFLAGS -o $cwrapper $cwrappersource - $STRIP $cwrapper - } - - # Now, create the wrapper script for func_source use: - func_ltwrapper_scriptname $cwrapper - $RM $func_ltwrapper_scriptname_result - trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 - $opt_dry_run || { - # note: this script will not be executed, so do not chmod. - if test "x$build" = "x$host" ; then - $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result - else - func_emit_wrapper no > $func_ltwrapper_scriptname_result - fi - } - ;; - * ) - $RM $output - trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 - - func_emit_wrapper no > $output - chmod +x $output - ;; - esac - } - exit $EXIT_SUCCESS - ;; - esac - - # See if we need to build an old-fashioned archive. - for oldlib in $oldlibs; do - - if test "$build_libtool_libs" = convenience; then - oldobjs="$libobjs_save $symfileobj" - addlibs="$convenience" - build_libtool_libs=no - else - if test "$build_libtool_libs" = module; then - oldobjs="$libobjs_save" - build_libtool_libs=no - else - oldobjs="$old_deplibs $non_pic_objects" - if test "$preload" = yes && test -f "$symfileobj"; then - func_append oldobjs " $symfileobj" - fi - fi - addlibs="$old_convenience" - fi - - if test -n "$addlibs"; then - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - - func_extract_archives $gentop $addlibs - func_append oldobjs " $func_extract_archives_result" - fi - - # Do each command in the archive commands. - if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then - cmds=$old_archive_from_new_cmds - else - - # Add any objects from preloaded convenience libraries - if test -n "$dlprefiles"; then - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - - func_extract_archives $gentop $dlprefiles - func_append oldobjs " $func_extract_archives_result" - fi - - # POSIX demands no paths to be encoded in archives. We have - # to avoid creating archives with duplicate basenames if we - # might have to extract them afterwards, e.g., when creating a - # static archive out of a convenience library, or when linking - # the entirety of a libtool archive into another (currently - # not supported by libtool). - if (for obj in $oldobjs - do - func_basename "$obj" - $ECHO "$func_basename_result" - done | sort | sort -uc >/dev/null 2>&1); then - : - else - echo "copying selected object files to avoid basename conflicts..." - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - func_mkdir_p "$gentop" - save_oldobjs=$oldobjs - oldobjs= - counter=1 - for obj in $save_oldobjs - do - func_basename "$obj" - objbase="$func_basename_result" - case " $oldobjs " in - " ") oldobjs=$obj ;; - *[\ /]"$objbase "*) - while :; do - # Make sure we don't pick an alternate name that also - # overlaps. - newobj=lt$counter-$objbase - func_arith $counter + 1 - counter=$func_arith_result - case " $oldobjs " in - *[\ /]"$newobj "*) ;; - *) if test ! -f "$gentop/$newobj"; then break; fi ;; - esac - done - func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" - func_append oldobjs " $gentop/$newobj" - ;; - *) func_append oldobjs " $obj" ;; - esac - done - fi - func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 - tool_oldlib=$func_to_tool_file_result - eval cmds=\"$old_archive_cmds\" - - func_len " $cmds" - len=$func_len_result - if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then - cmds=$old_archive_cmds - elif test -n "$archiver_list_spec"; then - func_verbose "using command file archive linking..." - for obj in $oldobjs - do - func_to_tool_file "$obj" - $ECHO "$func_to_tool_file_result" - done > $output_objdir/$libname.libcmd - func_to_tool_file "$output_objdir/$libname.libcmd" - oldobjs=" $archiver_list_spec$func_to_tool_file_result" - cmds=$old_archive_cmds - else - # the command line is too long to link in one step, link in parts - func_verbose "using piecewise archive linking..." - save_RANLIB=$RANLIB - RANLIB=: - objlist= - concat_cmds= - save_oldobjs=$oldobjs - oldobjs= - # Is there a better way of finding the last object in the list? - for obj in $save_oldobjs - do - last_oldobj=$obj - done - eval test_cmds=\"$old_archive_cmds\" - func_len " $test_cmds" - len0=$func_len_result - len=$len0 - for obj in $save_oldobjs - do - func_len " $obj" - func_arith $len + $func_len_result - len=$func_arith_result - func_append objlist " $obj" - if test "$len" -lt "$max_cmd_len"; then - : - else - # the above command should be used before it gets too long - oldobjs=$objlist - if test "$obj" = "$last_oldobj" ; then - RANLIB=$save_RANLIB - fi - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" - objlist= - len=$len0 - fi - done - RANLIB=$save_RANLIB - oldobjs=$objlist - if test "X$oldobjs" = "X" ; then - eval cmds=\"\$concat_cmds\" - else - eval cmds=\"\$concat_cmds~\$old_archive_cmds\" - fi - fi - fi - func_execute_cmds "$cmds" 'exit $?' - done - - test -n "$generated" && \ - func_show_eval "${RM}r$generated" - - # Now create the libtool archive. - case $output in - *.la) - old_library= - test "$build_old_libs" = yes && old_library="$libname.$libext" - func_verbose "creating $output" - - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - func_quote_for_eval "$var_value" - relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" - fi - done - # Quote the link command for shipping. - relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" - relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` - if test "$hardcode_automatic" = yes ; then - relink_command= - fi - - # Only create the output if not a dry run. - $opt_dry_run || { - for installed in no yes; do - if test "$installed" = yes; then - if test -z "$install_libdir"; then - break - fi - output="$output_objdir/$outputname"i - # Replace all uninstalled libtool libraries with the installed ones - newdependency_libs= - for deplib in $dependency_libs; do - case $deplib in - *.la) - func_basename "$deplib" - name="$func_basename_result" - func_resolve_sysroot "$deplib" - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` - test -z "$libdir" && \ - func_fatal_error "\`$deplib' is not a valid libtool archive" - func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" - ;; - -L*) - func_stripname -L '' "$deplib" - func_replace_sysroot "$func_stripname_result" - func_append newdependency_libs " -L$func_replace_sysroot_result" - ;; - -R*) - func_stripname -R '' "$deplib" - func_replace_sysroot "$func_stripname_result" - func_append newdependency_libs " -R$func_replace_sysroot_result" - ;; - *) func_append newdependency_libs " $deplib" ;; - esac - done - dependency_libs="$newdependency_libs" - newdlfiles= - - for lib in $dlfiles; do - case $lib in - *.la) - func_basename "$lib" - name="$func_basename_result" - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - test -z "$libdir" && \ - func_fatal_error "\`$lib' is not a valid libtool archive" - func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" - ;; - *) func_append newdlfiles " $lib" ;; - esac - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - case $lib in - *.la) - # Only pass preopened files to the pseudo-archive (for - # eventual linking with the app. that links it) if we - # didn't already link the preopened objects directly into - # the library: - func_basename "$lib" - name="$func_basename_result" - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - test -z "$libdir" && \ - func_fatal_error "\`$lib' is not a valid libtool archive" - func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" - ;; - esac - done - dlprefiles="$newdlprefiles" - else - newdlfiles= - for lib in $dlfiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - func_append newdlfiles " $abs" - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - func_append newdlprefiles " $abs" - done - dlprefiles="$newdlprefiles" - fi - $RM $output - # place dlname in correct position for cygwin - # In fact, it would be nice if we could use this code for all target - # systems that can't hard-code library paths into their executables - # and that have no shared library path variable independent of PATH, - # but it turns out we can't easily determine that from inspecting - # libtool variables, so we have to hard-code the OSs to which it - # applies here; at the moment, that means platforms that use the PE - # object format with DLL files. See the long comment at the top of - # tests/bindir.at for full details. - tdlname=$dlname - case $host,$output,$installed,$module,$dlname in - *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) - # If a -bindir argument was supplied, place the dll there. - if test "x$bindir" != x ; - then - func_relative_path "$install_libdir" "$bindir" - tdlname=$func_relative_path_result$dlname - else - # Otherwise fall back on heuristic. - tdlname=../bin/$dlname - fi - ;; - esac - $ECHO > $output "\ -# $outputname - a libtool library file -# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION -# -# Please DO NOT delete this file! -# It is necessary for linking the library. - -# The name that we can dlopen(3). -dlname='$tdlname' - -# Names of this library. -library_names='$library_names' - -# The name of the static archive. -old_library='$old_library' - -# Linker flags that can not go in dependency_libs. -inherited_linker_flags='$new_inherited_linker_flags' - -# Libraries that this one depends upon. -dependency_libs='$dependency_libs' - -# Names of additional weak libraries provided by this library -weak_library_names='$weak_libs' - -# Version information for $libname. -current=$current -age=$age -revision=$revision - -# Is this an already installed library? -installed=$installed - -# Should we warn about portability when linking against -modules? -shouldnotlink=$module - -# Files to dlopen/dlpreopen -dlopen='$dlfiles' -dlpreopen='$dlprefiles' - -# Directory that this library needs to be installed in: -libdir='$install_libdir'" - if test "$installed" = no && test "$need_relink" = yes; then - $ECHO >> $output "\ -relink_command=\"$relink_command\"" - fi - done - } - - # Do a symbolic link so that the libtool archive can be found in - # LD_LIBRARY_PATH before the program is installed. - func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' - ;; - esac - exit $EXIT_SUCCESS -} - -{ test "$opt_mode" = link || test "$opt_mode" = relink; } && - func_mode_link ${1+"$@"} - - -# func_mode_uninstall arg... -func_mode_uninstall () -{ - $opt_debug - RM="$nonopt" - files= - rmforce= - exit_status=0 - - # This variable tells wrapper scripts just to set variables rather - # than running their programs. - libtool_install_magic="$magic" - - for arg - do - case $arg in - -f) func_append RM " $arg"; rmforce=yes ;; - -*) func_append RM " $arg" ;; - *) func_append files " $arg" ;; - esac - done - - test -z "$RM" && \ - func_fatal_help "you must specify an RM program" - - rmdirs= - - for file in $files; do - func_dirname "$file" "" "." - dir="$func_dirname_result" - if test "X$dir" = X.; then - odir="$objdir" - else - odir="$dir/$objdir" - fi - func_basename "$file" - name="$func_basename_result" - test "$opt_mode" = uninstall && odir="$dir" - - # Remember odir for removal later, being careful to avoid duplicates - if test "$opt_mode" = clean; then - case " $rmdirs " in - *" $odir "*) ;; - *) func_append rmdirs " $odir" ;; - esac - fi - - # Don't error if the file doesn't exist and rm -f was used. - if { test -L "$file"; } >/dev/null 2>&1 || - { test -h "$file"; } >/dev/null 2>&1 || - test -f "$file"; then - : - elif test -d "$file"; then - exit_status=1 - continue - elif test "$rmforce" = yes; then - continue - fi - - rmfiles="$file" - - case $name in - *.la) - # Possibly a libtool archive, so verify it. - if func_lalib_p "$file"; then - func_source $dir/$name - - # Delete the libtool libraries and symlinks. - for n in $library_names; do - func_append rmfiles " $odir/$n" - done - test -n "$old_library" && func_append rmfiles " $odir/$old_library" - - case "$opt_mode" in - clean) - case " $library_names " in - *" $dlname "*) ;; - *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; - esac - test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" - ;; - uninstall) - if test -n "$library_names"; then - # Do each command in the postuninstall commands. - func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' - fi - - if test -n "$old_library"; then - # Do each command in the old_postuninstall commands. - func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' - fi - # FIXME: should reinstall the best remaining shared library. - ;; - esac - fi - ;; - - *.lo) - # Possibly a libtool object, so verify it. - if func_lalib_p "$file"; then - - # Read the .lo file - func_source $dir/$name - - # Add PIC object to the list of files to remove. - if test -n "$pic_object" && - test "$pic_object" != none; then - func_append rmfiles " $dir/$pic_object" - fi - - # Add non-PIC object to the list of files to remove. - if test -n "$non_pic_object" && - test "$non_pic_object" != none; then - func_append rmfiles " $dir/$non_pic_object" - fi - fi - ;; - - *) - if test "$opt_mode" = clean ; then - noexename=$name - case $file in - *.exe) - func_stripname '' '.exe' "$file" - file=$func_stripname_result - func_stripname '' '.exe' "$name" - noexename=$func_stripname_result - # $file with .exe has already been added to rmfiles, - # add $file without .exe - func_append rmfiles " $file" - ;; - esac - # Do a test to see if this is a libtool program. - if func_ltwrapper_p "$file"; then - if func_ltwrapper_executable_p "$file"; then - func_ltwrapper_scriptname "$file" - relink_command= - func_source $func_ltwrapper_scriptname_result - func_append rmfiles " $func_ltwrapper_scriptname_result" - else - relink_command= - func_source $dir/$noexename - fi - - # note $name still contains .exe if it was in $file originally - # as does the version of $file that was added into $rmfiles - func_append rmfiles " $odir/$name $odir/${name}S.${objext}" - if test "$fast_install" = yes && test -n "$relink_command"; then - func_append rmfiles " $odir/lt-$name" - fi - if test "X$noexename" != "X$name" ; then - func_append rmfiles " $odir/lt-${noexename}.c" - fi - fi - fi - ;; - esac - func_show_eval "$RM $rmfiles" 'exit_status=1' - done - - # Try to remove the ${objdir}s in the directories where we deleted files - for dir in $rmdirs; do - if test -d "$dir"; then - func_show_eval "rmdir $dir >/dev/null 2>&1" - fi - done - - exit $exit_status -} - -{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && - func_mode_uninstall ${1+"$@"} - -test -z "$opt_mode" && { - help="$generic_help" - func_fatal_help "you must specify a MODE" -} - -test -z "$exec_cmd" && \ - func_fatal_help "invalid operation mode \`$opt_mode'" - -if test -n "$exec_cmd"; then - eval exec "$exec_cmd" - exit $EXIT_FAILURE -fi - -exit $exit_status - - -# The TAGs below are defined such that we never get into a situation -# in which we disable both kinds of libraries. Given conflicting -# choices, we go for a static library, that is the most portable, -# since we can't tell whether shared libraries were disabled because -# the user asked for that or because the platform doesn't support -# them. This is particularly important on AIX, because we don't -# support having both static and shared libraries enabled at the same -# time on that platform, so we default to a shared-only configuration. -# If a disable-shared tag is given, we'll fallback to a static-only -# configuration. But we'll never go from static-only to shared-only. - -# ### BEGIN LIBTOOL TAG CONFIG: disable-shared -build_libtool_libs=no -build_old_libs=yes -# ### END LIBTOOL TAG CONFIG: disable-shared - -# ### BEGIN LIBTOOL TAG CONFIG: disable-static -build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` -# ### END LIBTOOL TAG CONFIG: disable-static - -# Local Variables: -# mode:shell-script -# sh-indentation:2 -# End: -# vi:sw=2 - diff --git a/build-aux/missing b/build-aux/missing deleted file mode 100755 index cdea514931..0000000000 --- a/build-aux/missing +++ /dev/null @@ -1,215 +0,0 @@ -#! /bin/sh -# Common wrapper for a few potentially missing GNU programs. - -scriptversion=2012-06-26.16; # UTC - -# Copyright (C) 1996-2013 Free Software Foundation, Inc. -# Originally written by Fran,cois Pinard , 1996. - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -if test $# -eq 0; then - echo 1>&2 "Try '$0 --help' for more information" - exit 1 -fi - -case $1 in - - --is-lightweight) - # Used by our autoconf macros to check whether the available missing - # script is modern enough. - exit 0 - ;; - - --run) - # Back-compat with the calling convention used by older automake. - shift - ;; - - -h|--h|--he|--hel|--help) - echo "\ -$0 [OPTION]... PROGRAM [ARGUMENT]... - -Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due -to PROGRAM being missing or too old. - -Options: - -h, --help display this help and exit - -v, --version output version information and exit - -Supported PROGRAM values: - aclocal autoconf autoheader autom4te automake makeinfo - bison yacc flex lex help2man - -Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and -'g' are ignored when checking the name. - -Send bug reports to ." - exit $? - ;; - - -v|--v|--ve|--ver|--vers|--versi|--versio|--version) - echo "missing $scriptversion (GNU Automake)" - exit $? - ;; - - -*) - echo 1>&2 "$0: unknown '$1' option" - echo 1>&2 "Try '$0 --help' for more information" - exit 1 - ;; - -esac - -# Run the given program, remember its exit status. -"$@"; st=$? - -# If it succeeded, we are done. -test $st -eq 0 && exit 0 - -# Also exit now if we it failed (or wasn't found), and '--version' was -# passed; such an option is passed most likely to detect whether the -# program is present and works. -case $2 in --version|--help) exit $st;; esac - -# Exit code 63 means version mismatch. This often happens when the user -# tries to use an ancient version of a tool on a file that requires a -# minimum version. -if test $st -eq 63; then - msg="probably too old" -elif test $st -eq 127; then - # Program was missing. - msg="missing on your system" -else - # Program was found and executed, but failed. Give up. - exit $st -fi - -perl_URL=http://www.perl.org/ -flex_URL=http://flex.sourceforge.net/ -gnu_software_URL=http://www.gnu.org/software - -program_details () -{ - case $1 in - aclocal|automake) - echo "The '$1' program is part of the GNU Automake package:" - echo "<$gnu_software_URL/automake>" - echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" - echo "<$gnu_software_URL/autoconf>" - echo "<$gnu_software_URL/m4/>" - echo "<$perl_URL>" - ;; - autoconf|autom4te|autoheader) - echo "The '$1' program is part of the GNU Autoconf package:" - echo "<$gnu_software_URL/autoconf/>" - echo "It also requires GNU m4 and Perl in order to run:" - echo "<$gnu_software_URL/m4/>" - echo "<$perl_URL>" - ;; - esac -} - -give_advice () -{ - # Normalize program name to check for. - normalized_program=`echo "$1" | sed ' - s/^gnu-//; t - s/^gnu//; t - s/^g//; t'` - - printf '%s\n' "'$1' is $msg." - - configure_deps="'configure.ac' or m4 files included by 'configure.ac'" - case $normalized_program in - autoconf*) - echo "You should only need it if you modified 'configure.ac'," - echo "or m4 files included by it." - program_details 'autoconf' - ;; - autoheader*) - echo "You should only need it if you modified 'acconfig.h' or" - echo "$configure_deps." - program_details 'autoheader' - ;; - automake*) - echo "You should only need it if you modified 'Makefile.am' or" - echo "$configure_deps." - program_details 'automake' - ;; - aclocal*) - echo "You should only need it if you modified 'acinclude.m4' or" - echo "$configure_deps." - program_details 'aclocal' - ;; - autom4te*) - echo "You might have modified some maintainer files that require" - echo "the 'automa4te' program to be rebuilt." - program_details 'autom4te' - ;; - bison*|yacc*) - echo "You should only need it if you modified a '.y' file." - echo "You may want to install the GNU Bison package:" - echo "<$gnu_software_URL/bison/>" - ;; - lex*|flex*) - echo "You should only need it if you modified a '.l' file." - echo "You may want to install the Fast Lexical Analyzer package:" - echo "<$flex_URL>" - ;; - help2man*) - echo "You should only need it if you modified a dependency" \ - "of a man page." - echo "You may want to install the GNU Help2man package:" - echo "<$gnu_software_URL/help2man/>" - ;; - makeinfo*) - echo "You should only need it if you modified a '.texi' file, or" - echo "any other file indirectly affecting the aspect of the manual." - echo "You might want to install the Texinfo package:" - echo "<$gnu_software_URL/texinfo/>" - echo "The spurious makeinfo call might also be the consequence of" - echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" - echo "want to install GNU make:" - echo "<$gnu_software_URL/make/>" - ;; - *) - echo "You might have modified some files without having the proper" - echo "tools for further handling them. Check the 'README' file, it" - echo "often tells you about the needed prerequisites for installing" - echo "this package. You may also peek at any GNU archive site, in" - echo "case some other package contains this missing '$1' program." - ;; - esac -} - -give_advice "$1" | sed -e '1s/^/WARNING: /' \ - -e '2,$s/^/ /' >&2 - -# Propagate the correct exit status (expected to be 127 for a program -# not found, 63 for a program that failed due to version mismatch). -exit $st - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" -# time-stamp-end: "; # UTC" -# End: diff --git a/cmake-format.yaml b/cmake-format.yaml new file mode 100644 index 0000000000..082e7b5ef1 --- /dev/null +++ b/cmake-format.yaml @@ -0,0 +1,48 @@ +parse: + additional_commands: + bout_add_example: + pargs: 1 + kwargs: + CONFLICTS: '*' + DATA_DIRS: '*' + EXTRA_FILES: '*' + REQUIRES: '*' + SOURCES: '*' + bout_add_integrated_test: + pargs: 1 + flags: [USE_DATA_BOUT_INP, USE_RUNTEST] + kwargs: + CONFLICTS: '*' + DOWNLOAD: 1 + DOWNLOAD_NAME: 1 + EXECUTABLE_NAME: 1 + EXTRA_DEPENDS: '*' + EXTRA_FILES: '*' + PROCESSORS: 1 + REQUIRES: '*' + SOURCES: '*' + TESTARGS: '*' + bout_add_mms_test: + pargs: 1 + flags: [USE_DATA_BOUT_INP, USE_RUNTEST] + kwargs: + CONFLICTS: '*' + DOWNLOAD: 1 + DOWNLOAD_NAME: 1 + EXECUTABLE_NAME: 1 + EXTRA_DEPENDS: '*' + EXTRA_FILES: '*' + PROCESSORS: 1 + REQUIRES: '*' + SOURCES: '*' + TESTARGS: '*' + bout_handle_requires_conflicts: + pargs: 2 + kwargs: + CONFLICTS: '*' + REQUIRES: '*' +format: + dangle_parens: true +markup: + # markup messes up comments - disable it + enable_markup: false \ No newline at end of file diff --git a/cmake/BOUT++functions.cmake b/cmake/BOUT++functions.cmake index 40e45f99be..9b64f643c6 100644 --- a/cmake/BOUT++functions.cmake +++ b/cmake/BOUT++functions.cmake @@ -3,9 +3,9 @@ # Copy FILENAME from source directory to build directory macro(bout_copy_file FILENAME) configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME} - ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME} - COPYONLY) + ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME} + ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME} COPYONLY + ) endmacro() # Handle the REQUIRES and CONFLICTS arguments for models, examples, @@ -15,16 +15,22 @@ macro(bout_handle_requires_conflicts TYPENAME TYPEVAR) set(multiValueArgs REQUIRES CONFLICTS) cmake_parse_arguments(BOUT_HANDLE_OPTIONS "" "" "${multiValueArgs}" ${ARGN}) - foreach (REQUIREMENT IN LISTS BOUT_HANDLE_OPTIONS_REQUIRES) - if (NOT ${REQUIREMENT}) - message(STATUS "Not building ${TYPENAME} ${TYPEVAR}, requirement not met: ${REQUIREMENT}") + foreach(REQUIREMENT IN LISTS BOUT_HANDLE_OPTIONS_REQUIRES) + if(NOT ${REQUIREMENT}) + message( + STATUS + "Not building ${TYPENAME} ${TYPEVAR}, requirement not met: ${REQUIREMENT}" + ) return() endif() endforeach() - foreach (CONFLICT IN LISTS BOUT_HANDLE_OPTIONS_CONFLICTS) - if (${CONFLICT}) - message(STATUS "Not building ${TYPENAME} ${TYPEVAR}, conflicts with: ${CONFLICT}") + foreach(CONFLICT IN LISTS BOUT_HANDLE_OPTIONS_CONFLICTS) + if(${CONFLICT}) + message( + STATUS + "Not building ${TYPENAME} ${TYPEVAR}, conflicts with: ${CONFLICT}" + ) return() endif() endforeach() @@ -46,25 +52,29 @@ function(bout_add_model MODEL) set(multiValueArgs SOURCES REQUIRES CONFLICTS) cmake_parse_arguments(BOUT_MODEL_OPTIONS "" "" "${multiValueArgs}" ${ARGN}) - bout_handle_requires_conflicts("model" MODEL + bout_handle_requires_conflicts( + "model" MODEL REQUIRES ${BOUT_MODEL_OPTIONS_REQUIRES} CONFLICTS ${BOUT_MODEL_OPTIONS_CONFLICTS} - ) + ) - if (NOT BOUT_MODEL_OPTIONS_SOURCES) - message(FATAL_ERROR "Required argument SOURCES missing from 'bout_add_model'") + if(NOT BOUT_MODEL_OPTIONS_SOURCES) + message( + FATAL_ERROR "Required argument SOURCES missing from 'bout_add_model'" + ) endif() - if ("SOURCES" IN_LIST BOUT_MODEL_OPTIONS_KEYWORDS_MISSING_VALUES) + if("SOURCES" IN_LIST BOUT_MODEL_OPTIONS_KEYWORDS_MISSING_VALUES) message(FATAL_ERROR "SOURCES missing values from 'bout_add_model'") endif() add_executable(${MODEL} ${BOUT_MODEL_OPTIONS_SOURCES}) target_link_libraries(${MODEL} bout++::bout++) - target_include_directories(${MODEL} PRIVATE $) + target_include_directories( + ${MODEL} PRIVATE $ + ) endfunction() - # Build a BOUT++ example # # If called from a standalone project, just builds the example as a @@ -85,37 +95,38 @@ function(bout_add_example EXAMPLENAME) set(multiValueArgs SOURCES REQUIRES CONFLICTS DATA_DIRS EXTRA_FILES) cmake_parse_arguments(BOUT_EXAMPLE_OPTIONS "" "" "${multiValueArgs}" ${ARGN}) - bout_handle_requires_conflicts("example" ${EXAMPLENAME} + bout_handle_requires_conflicts( + "example" ${EXAMPLENAME} REQUIRES ${BOUT_EXAMPLE_OPTIONS_REQUIRES} CONFLICTS ${BOUT_EXAMPLE_OPTIONS_CONFLICTS} - ) + ) bout_add_model(${EXAMPLENAME} SOURCES ${BOUT_EXAMPLE_OPTIONS_SOURCES}) # If this is a standalone project, we can stop here. Otherwise, we # need to copy the various input files to the build directory get_directory_property(HAS_PARENT PARENT_DIRECTORY) - if (NOT HAS_PARENT) + if(NOT HAS_PARENT) return() endif() # Copy the documentation if it exists - if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/README.md) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/README.md) bout_copy_file(README.md) endif() # Copy the input file - if (NOT BOUT_EXAMPLE_OPTIONS_DATA_DIRS) + if(NOT BOUT_EXAMPLE_OPTIONS_DATA_DIRS) bout_copy_file(data/BOUT.inp) else() - foreach (DATA_DIR IN LISTS BOUT_EXAMPLE_OPTIONS_DATA_DIRS) + foreach(DATA_DIR IN LISTS BOUT_EXAMPLE_OPTIONS_DATA_DIRS) bout_copy_file(${DATA_DIR}/BOUT.inp) endforeach() endif() # Copy any other needed files - if (BOUT_EXAMPLE_OPTIONS_EXTRA_FILES) - foreach (FILE ${BOUT_EXAMPLE_OPTIONS_EXTRA_FILES}) + if(BOUT_EXAMPLE_OPTIONS_EXTRA_FILES) + foreach(FILE ${BOUT_EXAMPLE_OPTIONS_EXTRA_FILES}) bout_copy_file("${FILE}") endforeach() endif() @@ -125,7 +136,6 @@ function(bout_add_example EXAMPLENAME) add_dependencies(build-all-examples ${EXAMPLENAME}) endfunction() - # Add a new integrated or MMS test. By default, the executable is # named like the first source, stripped of its file extension. If no # sources are given, then you probably at least want to set @@ -162,38 +172,52 @@ endfunction() # function(bout_add_integrated_or_mms_test BUILD_CHECK_TARGET TESTNAME) set(options USE_RUNTEST USE_DATA_BOUT_INP) - set(oneValueArgs EXECUTABLE_NAME PROCESSORS) - set(multiValueArgs SOURCES EXTRA_FILES REQUIRES CONFLICTS TESTARGS EXTRA_DEPENDS) - cmake_parse_arguments(BOUT_TEST_OPTIONS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(oneValueArgs EXECUTABLE_NAME PROCESSORS DOWNLOAD DOWNLOAD_NAME) + set(multiValueArgs SOURCES EXTRA_FILES REQUIRES CONFLICTS TESTARGS + EXTRA_DEPENDS + ) + cmake_parse_arguments( + BOUT_TEST_OPTIONS "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN} + ) - bout_handle_requires_conflicts("test" ${TESTNAME} + bout_handle_requires_conflicts( + "test" ${TESTNAME} REQUIRES ${BOUT_TEST_OPTIONS_REQUIRES} CONFLICTS ${BOUT_TEST_OPTIONS_CONFLICTS} - ) + ) - if (BOUT_TEST_OPTIONS_SOURCES) + if(BOUT_TEST_OPTIONS_SOURCES) # We've got some sources, so compile them into an executable and # link against BOUT++ add_executable(${TESTNAME} ${BOUT_TEST_OPTIONS_SOURCES}) target_link_libraries(${TESTNAME} bout++) - target_include_directories(${TESTNAME} PRIVATE $) + target_include_directories( + ${TESTNAME} PRIVATE $ + ) set_target_properties(${TESTNAME} PROPERTIES FOLDER tests/integrated) # Set the name of the executable. We either take it as an option, # or use the first source file, stripping the file suffix - if (BOUT_TEST_OPTIONS_EXECUTABLE_NAME) - set_target_properties(${TESTNAME} PROPERTIES OUTPUT_NAME ${BOUT_TEST_OPTIONS_EXECUTABLE_NAME}) + if(BOUT_TEST_OPTIONS_EXECUTABLE_NAME) + set_target_properties( + ${TESTNAME} PROPERTIES OUTPUT_NAME ${BOUT_TEST_OPTIONS_EXECUTABLE_NAME} + ) else() # If more than one source file, just get the first one list(LENGTH ${BOUT_TEST_OPTIONS_SOURCES} BOUT_SOURCES_LENGTH) - if (BOUT_SOURCES_LENGTH GREATER 0) + if(BOUT_SOURCES_LENGTH GREATER 0) list(GET ${BOUT_TEST_OPTIONS_SOURCES} 0 BOUT_TEST_FIRST_SOURCE) else() set(BOUT_TEST_FIRST_SOURCE ${BOUT_TEST_OPTIONS_SOURCES}) endif() # Strip the directory and file extension from the source file - get_filename_component(BOUT_TEST_EXECUTABLE_NAME ${BOUT_TEST_FIRST_SOURCE} NAME_WE) - set_target_properties(${TESTNAME} PROPERTIES OUTPUT_NAME ${BOUT_TEST_EXECUTABLE_NAME}) + get_filename_component( + BOUT_TEST_EXECUTABLE_NAME ${BOUT_TEST_FIRST_SOURCE} NAME_WE + ) + set_target_properties( + ${TESTNAME} PROPERTIES OUTPUT_NAME ${BOUT_TEST_EXECUTABLE_NAME} + ) endif() # Add the test to the build-check-integrated-tests target @@ -202,40 +226,59 @@ function(bout_add_integrated_or_mms_test BUILD_CHECK_TARGET TESTNAME) add_custom_target(${TESTNAME}) endif() - if (BOUT_TEST_OPTIONS_EXTRA_DEPENDS) + if(BOUT_TEST_OPTIONS_DOWNLOAD) + if(NOT BOUT_TEST_OPTIONS_DOWNLOAD_NAME) + message(FATAL_ERROR "We need DOWNLOAD_NAME if we should DOWNLOAD!") + endif() + set(output) + add_custom_command( + OUTPUT ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} + COMMAND + wget ${BOUT_TEST_OPTIONS_DOWNLOAD} -O ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} + $ENV{BOUT_TEST_DOWNLOAD_FLAGS} || curl ${BOUT_TEST_OPTIONS_DOWNLOAD} -o + ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Downloading ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME}" + ) + add_custom_target( + download_test_data DEPENDS ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} + ) + add_dependencies(${TESTNAME} download_test_data) + endif() + + if(BOUT_TEST_OPTIONS_EXTRA_DEPENDS) add_dependencies(${TESTNAME} ${BOUT_TEST_OPTIONS_EXTRA_DEPENDS}) endif() - if (NOT BOUT_TEST_OPTIONS_PROCESSORS) + if(NOT BOUT_TEST_OPTIONS_PROCESSORS) set(BOUT_TEST_OPTIONS_PROCESSORS 1) endif() # Set the actual test command - if (BOUT_TEST_OPTIONS_USE_RUNTEST) - add_test(NAME ${TESTNAME} - COMMAND ./runtest ${BOUT_TEST_OPTIONS_TESTARGS} - ) - set_tests_properties(${TESTNAME} PROPERTIES - ENVIRONMENT PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} - ) + if(BOUT_TEST_OPTIONS_USE_RUNTEST) + add_test(NAME ${TESTNAME} COMMAND ./runtest ${BOUT_TEST_OPTIONS_TESTARGS}) + set_tests_properties( + ${TESTNAME} PROPERTIES ENVIRONMENT + PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} + ) bout_copy_file(runtest) else() add_test(NAME ${TESTNAME} COMMAND ${TESTNAME} ${BOUT_TEST_OPTIONS_TESTARGS}) endif() - set_tests_properties(${TESTNAME} PROPERTIES - PROCESSORS ${BOUT_TEST_OPTIONS_PROCESSORS} - PROCESSOR_AFFINITY ON - ) + set_tests_properties( + ${TESTNAME} PROPERTIES PROCESSORS ${BOUT_TEST_OPTIONS_PROCESSORS} + PROCESSOR_AFFINITY ON + ) # Copy the input file if needed - if (BOUT_TEST_OPTIONS_USE_DATA_BOUT_INP) + if(BOUT_TEST_OPTIONS_USE_DATA_BOUT_INP) bout_copy_file(data/BOUT.inp) endif() # Copy any other needed files - if (BOUT_TEST_OPTIONS_EXTRA_FILES) - foreach (FILE ${BOUT_TEST_OPTIONS_EXTRA_FILES}) + if(BOUT_TEST_OPTIONS_EXTRA_FILES) + foreach(FILE ${BOUT_TEST_OPTIONS_EXTRA_FILES}) bout_copy_file("${FILE}") endforeach() endif() @@ -243,7 +286,9 @@ endfunction() # Add a new integrated test. See `bout_add_integrated_or_mms_test` for arguments function(bout_add_integrated_test TESTNAME) - bout_add_integrated_or_mms_test(build-check-integrated-tests ${TESTNAME} ${ARGV}) + bout_add_integrated_or_mms_test( + build-check-integrated-tests ${TESTNAME} ${ARGV} + ) endfunction() # Add a new MMS test. See `bout_add_integrated_or_mms_test` for arguments @@ -256,13 +301,18 @@ endfunction() # Taken from https://github.com/conan-io/conan/issues/2125#issuecomment-351176653 function(bout_add_library_alias dst src) add_library(${dst} INTERFACE IMPORTED) - foreach(name INTERFACE_LINK_LIBRARIES INTERFACE_INCLUDE_DIRECTORIES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS) - get_property(value TARGET ${src} PROPERTY ${name} ) + foreach(name INTERFACE_LINK_LIBRARIES INTERFACE_INCLUDE_DIRECTORIES + INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS + ) + get_property( + value + TARGET ${src} + PROPERTY ${name} + ) set_property(TARGET ${dst} PROPERTY ${name} ${value}) endforeach() endfunction() - # Call nx-config with an argument, and append the resulting path to a list # Taken from https://github.com/LiamBindle/geos-chem/blob/feature/CMake/CMakeScripts/FindNetCDF.cmake function(bout_inspect_netcdf_config VAR NX_CONFIG ARG) @@ -271,7 +321,10 @@ function(bout_inspect_netcdf_config VAR NX_CONFIG ARG) OUTPUT_VARIABLE NX_CONFIG_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ) - if (NX_CONFIG_OUTPUT) - set(${VAR} ${NX_CONFIG_OUTPUT} PARENT_SCOPE) + if(NX_CONFIG_OUTPUT) + set(${VAR} + ${NX_CONFIG_OUTPUT} + PARENT_SCOPE + ) endif() endfunction() diff --git a/cmake/BuildType.cmake b/cmake/BuildType.cmake index c4dd7ff73d..7aced6d8ad 100644 --- a/cmake/BuildType.cmake +++ b/cmake/BuildType.cmake @@ -12,10 +12,17 @@ set(default_build_type "RelWithDebInfo") if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to '${default_build_type}' as none was specified.") - set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE - STRING "Choose the type of build." FORCE) + message( + STATUS + "Setting build type to '${default_build_type}' as none was specified." + ) + set(CMAKE_BUILD_TYPE + "${default_build_type}" + CACHE STRING "Choose the type of build." FORCE + ) # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo") + set_property( + CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" + "RelWithDebInfo" + ) endif() diff --git a/cmake/CorrectWindowsPaths.cmake b/cmake/CorrectWindowsPaths.cmake index 09bcdd67dc..cd2ac529ca 100644 --- a/cmake/CorrectWindowsPaths.cmake +++ b/cmake/CorrectWindowsPaths.cmake @@ -4,11 +4,9 @@ # This uses the command cygpath (provided by cygwin) to convert # unix-style paths into paths useable by cmake on windows -macro (CONVERT_CYGWIN_PATH _path) - if (WIN32) - EXECUTE_PROCESS(COMMAND cygpath.exe -m ${${_path}} - OUTPUT_VARIABLE ${_path}) - string (STRIP ${${_path}} ${_path}) - endif (WIN32) -endmacro (CONVERT_CYGWIN_PATH) - +macro(CONVERT_CYGWIN_PATH _path) + if(WIN32) + execute_process(COMMAND cygpath.exe -m ${${_path}} OUTPUT_VARIABLE ${_path}) + string(STRIP ${${_path}} ${_path}) + endif(WIN32) +endmacro(CONVERT_CYGWIN_PATH) diff --git a/cmake/EnableCXXWarningIfSupport.cmake b/cmake/EnableCXXWarningIfSupport.cmake index 6d7a64265f..9b9ae52af5 100644 --- a/cmake/EnableCXXWarningIfSupport.cmake +++ b/cmake/EnableCXXWarningIfSupport.cmake @@ -5,7 +5,7 @@ function(target_enable_cxx_warning_if_supported TARGET) set(multiValueArgs FLAGS) cmake_parse_arguments(TARGET_ENABLE_WARNING "" "" "${multiValueArgs}" ${ARGN}) - foreach (WARNING_FLAG IN LISTS TARGET_ENABLE_WARNING_FLAGS) + foreach(WARNING_FLAG IN LISTS TARGET_ENABLE_WARNING_FLAGS) string(REPLACE "-" "_" WARNING_FLAG_STRIPPED ${WARNING_FLAG}) # Note that gcc ignores unknown flags of the form "-Wno-warning" @@ -13,31 +13,33 @@ function(target_enable_cxx_warning_if_supported TARGET) # positive form as an additional flag which it will choke on (if # it doesn't exist). See: https://gcc.gnu.org/wiki/FAQ#wnowarning string(FIND ${WARNING_FLAG} "Wno-" NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) - if (NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED} EQUAL -1) + if(NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED} EQUAL -1) set(IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED} FALSE) else() set(IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED} TRUE) endif() - if (IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) + if(IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) set(ORIGINAL_FLAG ${WARNING_FLAG}) string(REPLACE "no-" "" WARNING_FLAG ${WARNING_FLAG}) message(STATUS "Found negative flag: ${ORIGINAL_FLAG}\n" - " replaced with ${WARNING_FLAG}") + " replaced with ${WARNING_FLAG}" + ) endif() check_cxx_compiler_flag(${WARNING_FLAG} HAS_FLAG_${WARNING_FLAG_STRIPPED}) - if (IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) + if(IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) set(WARNING_FLAG ${ORIGINAL_FLAG}) endif() - if (HAS_FLAG_${WARNING_FLAG_STRIPPED}) - message(STATUS "Warning flag is supported by compiler: ${WARNING_FLAG}") + if(HAS_FLAG_${WARNING_FLAG_STRIPPED}) + message(STATUS "Warning flag is supported by compiler: ${WARNING_FLAG}") - target_compile_options(${TARGET} PRIVATE - $<$>:${WARNING_FLAG} > - $<$:-Xcompiler=${WARNING_FLAG} > + target_compile_options( + ${TARGET} + PRIVATE $<$>:${WARNING_FLAG} > + $<$:-Xcompiler=${WARNING_FLAG} > ) else() message(STATUS "Warning flag not supported by compiler: ${WARNING_FLAG}") diff --git a/cmake/FindBash.cmake b/cmake/FindBash.cmake index feb195f9f1..faa5303646 100644 --- a/cmake/FindBash.cmake +++ b/cmake/FindBash.cmake @@ -11,27 +11,30 @@ # Bash_VERSION - Bash version # Bash_EXECUTABLE - Path to bash executable -find_program(Bash_EXECUTABLE - bash - ) +find_program(Bash_EXECUTABLE bash) mark_as_advanced(Bash_EXECUTABLE) -if (Bash_EXECUTABLE) - execute_process(COMMAND "${Bash_EXECUTABLE}" --version +if(Bash_EXECUTABLE) + execute_process( + COMMAND "${Bash_EXECUTABLE}" --version RESULT_VARIABLE _bash_runs OUTPUT_VARIABLE _bash_stdout OUTPUT_STRIP_TRAILING_WHITESPACE - ) + ) if(_bash_stdout MATCHES "version ([0-9]+\\.[0-9]+\\.[0-9]+)") set(Bash_VERSION "${CMAKE_MATCH_1}") else() - message (WARNING "Failed to determine version of Bash interpreter (${Bash_EXECUTABLE})! Error:\n${_Bash_STDERR}") + message( + WARNING + "Failed to determine version of Bash interpreter (${Bash_EXECUTABLE})! Error:\n${_Bash_STDERR}" + ) endif() endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Bash +find_package_handle_standard_args( + Bash VERSION_VAR Bash_VERSION REQUIRED_VARS Bash_EXECUTABLE - ) +) diff --git a/cmake/FindClangFormat.cmake b/cmake/FindClangFormat.cmake index c940002c3b..cd5ca5865b 100644 --- a/cmake/FindClangFormat.cmake +++ b/cmake/FindClangFormat.cmake @@ -3,27 +3,23 @@ # Taken from https://github.com/ttroy50/cmake-examples commit 64bd54a # This file is under MIT Licence -if (NOT ClangFormat_BIN_NAME) +if(NOT ClangFormat_BIN_NAME) set(ClangFormat_BIN_NAME clang-format) endif() # if custom path check there first -if (ClangFormat_ROOT_DIR) - find_program(ClangFormat_BIN - NAMES - ${ClangFormat_BIN_NAME} - PATHS - "${ClangFormat_ROOT_DIR}" - NO_DEFAULT_PATH) +if(ClangFormat_ROOT_DIR) + find_program( + ClangFormat_BIN + NAMES ${ClangFormat_BIN_NAME} + PATHS "${ClangFormat_ROOT_DIR}" + NO_DEFAULT_PATH + ) endif() find_program(ClangFormat_BIN NAMES ${ClangFormat_BIN_NAME}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - ClangFormat - DEFAULT_MSG - ClangFormat_BIN) +find_package_handle_standard_args(ClangFormat DEFAULT_MSG ClangFormat_BIN) -mark_as_advanced( - ClangFormat_BIN) +mark_as_advanced(ClangFormat_BIN) diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake index 3b98cde89e..5a58cf4335 100644 --- a/cmake/FindCython.cmake +++ b/cmake/FindCython.cmake @@ -10,20 +10,22 @@ # CYTHON_FOUND - true if Cython was found # CYTHON_VERSION - Cython version -execute_process(COMMAND ${Python3_EXECUTABLE} -c "import cython ; print(cython.__version__)" +execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import cython ; print(cython.__version__)" RESULT_VARIABLE _cython_runs OUTPUT_VARIABLE CYTHON_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ) +) -if (${_cython_runs} EQUAL 0) +if(${_cython_runs} EQUAL 0) set(CYTHON_RUNS TRUE) else() set(CYTHON_RUNS FALSE) endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Cython +find_package_handle_standard_args( + Cython VERSION_VAR CYTHON_VERSION REQUIRED_VARS CYTHON_RUNS - ) +) diff --git a/cmake/FindFFTW.cmake b/cmake/FindFFTW.cmake index e1940c687d..7ca5518a6f 100644 --- a/cmake/FindFFTW.cmake +++ b/cmake/FindFFTW.cmake @@ -24,72 +24,78 @@ # ``FFTW_DEBUG`` # Set to TRUE to get extra debugging output -if (FFTW_INCLUDE_DIRS) +if(FFTW_INCLUDE_DIRS) # Already in cache, be silent - set (FFTW_FIND_QUIETLY TRUE) -endif (FFTW_INCLUDE_DIRS) + set(FFTW_FIND_QUIETLY TRUE) +endif(FFTW_INCLUDE_DIRS) -if (EXISTS ${FFTW_ROOT}) +if(EXISTS ${FFTW_ROOT}) # Make sure FFTW_ROOT is an absolute path by setting it as a 'FILEPATH' - set (FFTW_ROOT "" CACHE FILEPATH "Location of the FFTW library") + set(FFTW_ROOT + "" + CACHE FILEPATH "Location of the FFTW library" + ) endif() -find_program(FFTW_WISDOM "fftw-wisdom" +find_program( + FFTW_WISDOM "fftw-wisdom" PATHS "${FFTW_ROOT}" PATH_SUFFIXES bin NO_DEFAULT_PATH DOC "Path to fftw-wisdom executable" - ) +) -find_program(FFTW_WISDOM "fftw-wisdom" - DOC "Path to fftw-wisdom executable" - ) -if (FFTW_DEBUG) +find_program(FFTW_WISDOM "fftw-wisdom" DOC "Path to fftw-wisdom executable") +if(FFTW_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " FFTW_WISDOM = ${FFTW_WISDOM}" - ) + " FFTW_WISDOM = ${FFTW_WISDOM}" + ) endif() get_filename_component(FFTW_WISDOM_TMP "${FFTW_WISDOM}" DIRECTORY) get_filename_component(FFTW_HINT_DIR "${FFTW_WISDOM_TMP}" DIRECTORY) -find_path(FFTW_INCLUDE_DIRS +find_path( + FFTW_INCLUDE_DIRS NAMES fftw3.h DOC "FFTW include directory" HINTS "${FFTW_HINT_DIR}" PATH_SUFFIXES "include" - ) -if (FFTW_DEBUG) +) +if(FFTW_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " FFTW_INCLUDE_DIRS = ${FFTW_INCLUDE_DIRS}" - " FFTW_HINT_DIR = ${FFTW_HINT_DIR}" - ) + " FFTW_INCLUDE_DIRS = ${FFTW_INCLUDE_DIRS}" + " FFTW_HINT_DIR = ${FFTW_HINT_DIR}" + ) endif() -find_library (FFTW_LIBRARIES +find_library( + FFTW_LIBRARIES NAMES fftw3 DOC "FFTW library location" HINTS "${FFTW_HINT_DIR}" PATH_SUFFIXES "lib" "lib64" - ) -if (FFTW_DEBUG) +) +if(FFTW_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " FFTW_LIBRARIES = ${FFTW_LIBRARIES}" - " FFTW_HINT_DIR = ${FFTW_HINT_DIR}" - ) + " FFTW_LIBRARIES = ${FFTW_LIBRARIES}" + " FFTW_HINT_DIR = ${FFTW_HINT_DIR}" + ) endif() # handle the QUIETLY and REQUIRED arguments and set FFTW_FOUND to TRUE if # all listed variables are TRUE -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args (FFTW DEFAULT_MSG FFTW_LIBRARIES FFTW_INCLUDE_DIRS) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + FFTW DEFAULT_MSG FFTW_LIBRARIES FFTW_INCLUDE_DIRS +) -mark_as_advanced (FFTW_LIBRARIES FFTW_INCLUDE_DIRS) +mark_as_advanced(FFTW_LIBRARIES FFTW_INCLUDE_DIRS) -if (FFTW_FOUND AND NOT TARGET FFTW::FFTW) +if(FFTW_FOUND AND NOT TARGET FFTW::FFTW) add_library(FFTW::FFTW UNKNOWN IMPORTED) - set_target_properties(FFTW::FFTW PROPERTIES - IMPORTED_LOCATION "${FFTW_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" - ) + set_target_properties( + FFTW::FFTW PROPERTIES IMPORTED_LOCATION "${FFTW_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" + ) endif() diff --git a/cmake/FindHYPRE.cmake b/cmake/FindHYPRE.cmake index 1b9a5ca6f9..d2a1b4746c 100644 --- a/cmake/FindHYPRE.cmake +++ b/cmake/FindHYPRE.cmake @@ -6,54 +6,62 @@ include(FindPackageHandleStandardArgs) find_package(HYPRE CONFIG QUIET) -if (HYPRE_FOUND) +if(HYPRE_FOUND) message(STATUS "Found HYPRE: ${HYPRE_VERSION}") return() endif() -find_path(HYPRE_INCLUDE_DIR +find_path( + HYPRE_INCLUDE_DIR NAMES HYPRE.h - DOC "HYPRE include directories" - REQUIRED + DOC "HYPRE include directories" REQUIRED PATH_SUFFIXES include include/hypre ) -find_library(HYPRE_LIBRARY +find_library( + HYPRE_LIBRARY NAMES HYPRE - DOC "HYPRE library" - REQUIRED + DOC "HYPRE library" REQUIRED PATH_SUFFIXES lib64 lib - ) +) -if (HYPRE_INCLUDE_DIR) +if(HYPRE_INCLUDE_DIR) file(READ "${HYPRE_INCLUDE_DIR}/HYPRE_config.h" HYPRE_CONFIG_FILE) - string(REGEX MATCH ".*#define HYPRE_RELEASE_VERSION \"([0-9]+)\\.([0-9]+)\\.([0-9]+)\".*" - _ "${HYPRE_CONFIG_FILE}") + string( + REGEX MATCH + ".*#define HYPRE_RELEASE_VERSION \"([0-9]+)\\.([0-9]+)\\.([0-9]+)\".*" + _ "${HYPRE_CONFIG_FILE}" + ) set(HYPRE_VERSION_MAJOR ${CMAKE_MATCH_1}) set(HYPRE_VERSION_MINOR ${CMAKE_MATCH_2}) set(HYPRE_VERSION_PATCH ${CMAKE_MATCH_3}) - set(HYPRE_VERSION "${HYPRE_VERSION_MAJOR}.${HYPRE_VERSION_MINOR}.${HYPRE_VERSION_PATCH}") + set(HYPRE_VERSION + "${HYPRE_VERSION_MAJOR}.${HYPRE_VERSION_MINOR}.${HYPRE_VERSION_PATCH}" + ) endif() -if (HYPRE_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ]" - " HYPRE_ROOT = ${HYPRE_ROOT}" - " HYPRE_INCLUDE_DIR = ${HYPRE_INCLUDE_DIR}" - " HYPRE_LIBRARY = ${HYPRE_LIBRARY}" - ) +if(HYPRE_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ]" + " HYPRE_ROOT = ${HYPRE_ROOT}" + " HYPRE_INCLUDE_DIR = ${HYPRE_INCLUDE_DIR}" + " HYPRE_LIBRARY = ${HYPRE_LIBRARY}" + ) endif() mark_as_advanced(HYPRE_INCLUDE_DIR HYPRE_LIBRARY) -find_package_handle_standard_args(HYPRE +find_package_handle_standard_args( + HYPRE REQUIRED_VARS HYPRE_LIBRARY HYPRE_INCLUDE_DIR VERSION_VAR HYPRE_VERSION - ) +) -if (HYPRE_FOUND AND NOT TARGET HYPRE::HYPRE) +if(HYPRE_FOUND AND NOT TARGET HYPRE::HYPRE) add_library(HYPRE::HYPRE UNKNOWN IMPORTED) - set_target_properties(HYPRE::HYPRE PROPERTIES - IMPORTED_LOCATION "${HYPRE_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${HYPRE_INCLUDE_DIR}" - ) + set_target_properties( + HYPRE::HYPRE + PROPERTIES IMPORTED_LOCATION "${HYPRE_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${HYPRE_INCLUDE_DIR}" + ) endif() diff --git a/cmake/FindLibuuid.cmake b/cmake/FindLibuuid.cmake index 91880487a2..2492388b32 100644 --- a/cmake/FindLibuuid.cmake +++ b/cmake/FindLibuuid.cmake @@ -21,38 +21,41 @@ # ``Libuuid_DEBUG`` # Set to TRUE to get extra debugging output -include (FindPackageHandleStandardArgs) +include(FindPackageHandleStandardArgs) -if (WIN32) +if(WIN32) find_package_handle_standard_args(Libuuid DEFAULT_MSG) return() endif() -if (APPLE) +if(APPLE) find_library(CFLIB CoreFoundation) find_package_handle_standard_args(Libuuid DEFAULT_MSG CFLIB) mark_as_advanced(${CFLIB}) - if (Libuuid_FOUND AND NOT TARGET Libuuid::libuuid) + if(Libuuid_FOUND AND NOT TARGET Libuuid::libuuid) add_library(Libuuid::libuuid UNKNOWN IMPORTED) - set_target_properties(Libuuid::libuuid PROPERTIES - IMPORTED_LOCATION ${CFLIB} - ) + set_target_properties( + Libuuid::libuuid PROPERTIES IMPORTED_LOCATION ${CFLIB} + ) endif() return() -endif () +endif() find_path(Libuuid_INCLUDE_DIRS uuid/uuid.h) find_library(Libuuid_LIBRARIES uuid) -find_package_handle_standard_args(Libuuid DEFAULT_MSG Libuuid_LIBRARIES Libuuid_INCLUDE_DIRS) +find_package_handle_standard_args( + Libuuid DEFAULT_MSG Libuuid_LIBRARIES Libuuid_INCLUDE_DIRS +) mark_as_advanced(Libuuid_LIBRARIES Libuuid_INCLUDE_DIRS) -if (Libuuid_FOUND AND NOT TARGET Libuuid::libuuid) +if(Libuuid_FOUND AND NOT TARGET Libuuid::libuuid) add_library(Libuuid::libuuid UNKNOWN IMPORTED) - set_target_properties(Libuuid::libuuid PROPERTIES - IMPORTED_LOCATION "${Libuuid_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${Libuuid_INCLUDE_DIRS}" - ) + set_target_properties( + Libuuid::libuuid + PROPERTIES IMPORTED_LOCATION "${Libuuid_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${Libuuid_INCLUDE_DIRS}" + ) endif() diff --git a/cmake/FindNumpy.cmake b/cmake/FindNumpy.cmake index b6de6e3e35..cfdbdcc96c 100644 --- a/cmake/FindNumpy.cmake +++ b/cmake/FindNumpy.cmake @@ -11,46 +11,57 @@ # Numpy_VERSION # Numpy_INCLUDE_DIR - find_package(Python3 3.6 COMPONENTS Interpreter Development) -if (NOT Python3_FOUND) - message(STATUS "Could not find numpy as python was not found. Maybe the developement package is missing?") +if(NOT Python3_FOUND) + message( + STATUS + "Could not find numpy as python was not found. Maybe the developement package is missing?" + ) set(Numpy_FOUND ${Python3_FOUND}) return() endif() -if (NOT Numpy_FOUND) - execute_process(COMMAND ${Python3_EXECUTABLE} -c "import numpy ; print(numpy.__version__)" +if(NOT Numpy_FOUND) + execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import numpy ; print(numpy.__version__)" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE Numpy_VERSION - ) - execute_process(COMMAND ${Python3_EXECUTABLE} -c "import numpy ; print(numpy.get_include())" + ) + execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import numpy ; print(numpy.get_include())" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE _numpy_include_dirs - ) + ) endif() -if (Numpy_DEBUG) - message(STATUS "Looking for numpy headers in: ${_numpy_include_dirs} ${Python3_INCLUDE_DIRS}") +if(Numpy_DEBUG) + message( + STATUS + "Looking for numpy headers in: ${_numpy_include_dirs} ${Python3_INCLUDE_DIRS}" + ) endif() -find_path(Numpy_INCLUDE_DIR - numpy/arrayobject.h +find_path( + Numpy_INCLUDE_DIR numpy/arrayobject.h PATHS "${_numpy_include_dirs}" "${Python3_INCLUDE_DIRS}" PATH_SUFFIXES numpy/core/include - ) +) -if (NOT Numpy_INCLUDE_DIR) - message(STATUS "Numpy headers not found -- do you need to install the development package?") +if(NOT Numpy_INCLUDE_DIR) + message( + STATUS + "Numpy headers not found -- do you need to install the development package?" + ) endif() set(Numpy_INCLUDE_DIRS ${Numpy_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Numpy +find_package_handle_standard_args( + Numpy VERSION_VAR Numpy_VERSION REQUIRED_VARS Numpy_INCLUDE_DIR - ) +) mark_as_advanced(Numpy_INCLUDE_DIR) diff --git a/cmake/FindPETSc.cmake b/cmake/FindPETSc.cmake index c93d464673..f0595a7a68 100644 --- a/cmake/FindPETSc.cmake +++ b/cmake/FindPETSc.cmake @@ -25,23 +25,23 @@ find_package(MPI REQUIRED) -set(PETSC_VALID_COMPONENTS - C - CXX) +set(PETSC_VALID_COMPONENTS C CXX) if(NOT PETSc_FIND_COMPONENTS) - get_property (_enabled_langs GLOBAL PROPERTY ENABLED_LANGUAGES) - if ("C" IN_LIST _enabled_langs) + get_property(_enabled_langs GLOBAL PROPERTY ENABLED_LANGUAGES) + if("C" IN_LIST _enabled_langs) set(PETSC_LANGUAGE_BINDINGS "C") - else () + else() set(PETSC_LANGUAGE_BINDINGS "CXX") - endif () + endif() else() # Right now, this is designed for compatability with the --with-clanguage option, so # only allow one item in the components list. list(LENGTH ${PETSc_FIND_COMPONENTS} components_length) if(${components_length} GREATER 1) - message(FATAL_ERROR "Only one component for PETSc is allowed to be specified") + message( + FATAL_ERROR "Only one component for PETSc is allowed to be specified" + ) endif() # This is a stub for allowing multiple components should that time ever come. Perhaps # to also test Fortran bindings? @@ -61,218 +61,328 @@ if(NOT PETSC_DIR) endif() endif() -function (petsc_get_version) - if (EXISTS "${PETSC_DIR}/include/petscversion.h") - file (STRINGS "${PETSC_DIR}/include/petscversion.h" vstrings REGEX "#define PETSC_VERSION_(RELEASE|MAJOR|MINOR|SUBMINOR|PATCH) ") - foreach (line ${vstrings}) - string (REGEX REPLACE " +" ";" fields ${line}) # break line into three fields (the first is always "#define") - list (GET fields 1 var) - list (GET fields 2 val) - set (${var} ${val} PARENT_SCOPE) - set (${var} ${val}) # Also in local scope so we have access below - endforeach () - if (PETSC_VERSION_RELEASE) - if ($(PETSC_VERSION_PATCH) GREATER 0) - set (PETSC_VERSION "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}p${PETSC_VERSION_PATCH}" CACHE INTERNAL "PETSc version") - else () - set (PETSC_VERSION "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}" CACHE INTERNAL "PETSc version") - endif () - else () +function(petsc_get_version) + if(EXISTS "${PETSC_DIR}/include/petscversion.h") + file(STRINGS "${PETSC_DIR}/include/petscversion.h" vstrings + REGEX "#define PETSC_VERSION_(RELEASE|MAJOR|MINOR|SUBMINOR|PATCH) " + ) + foreach(line ${vstrings}) + string(REGEX REPLACE " +" ";" fields ${line} + )# break line into three fields (the first is always "#define") + list(GET fields 1 var) + list(GET fields 2 val) + set(${var} + ${val} + PARENT_SCOPE + ) + set(${var} ${val}) # Also in local scope so we have access below + endforeach() + if(PETSC_VERSION_RELEASE) + if($(PETSC_VERSION_PATCH) GREATER 0) + set(PETSC_VERSION + "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}p${PETSC_VERSION_PATCH}" + CACHE INTERNAL "PETSc version" + ) + else() + set(PETSC_VERSION + "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}" + CACHE INTERNAL "PETSc version" + ) + endif() + else() # make dev version compare higher than any patch level of a released version - set (PETSC_VERSION "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}.99" CACHE INTERNAL "PETSc version") - endif () - else () - message (SEND_ERROR "PETSC_DIR can not be used, ${PETSC_DIR}/include/petscversion.h does not exist") - endif () -endfunction () + set(PETSC_VERSION + "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}.99" + CACHE INTERNAL "PETSc version" + ) + endif() + else() + message( + SEND_ERROR + "PETSC_DIR can not be used, ${PETSC_DIR}/include/petscversion.h does not exist" + ) + endif() +endfunction() # Debian uses versioned paths e.g /usr/lib/petscdir/3.5/ -file (GLOB DEB_PATHS "/usr/lib/petscdir/*") +file(GLOB DEB_PATHS "/usr/lib/petscdir/*") -find_path (PETSC_DIR include/petsc.h +find_path( + PETSC_DIR include/petsc.h HINTS ENV PETSC_DIR - PATHS - /usr/lib/petsc - # Debian paths - ${DEB_PATHS} - # Arch Linux path - /opt/petsc/linux-c-opt - # MacPorts path - /opt/local/lib/petsc - $ENV{HOME}/petsc - DOC "PETSc Directory") - -find_program (MAKE_EXECUTABLE NAMES make gmake) - -if (PETSC_DIR AND NOT PETSC_ARCH) - set (_petsc_arches - $ENV{PETSC_ARCH} # If set, use environment variable first - linux-gnu-c-debug linux-gnu-c-opt # Debian defaults - x86_64-unknown-linux-gnu i386-unknown-linux-gnu) - set (petscconf "NOTFOUND" CACHE FILEPATH "Cleared" FORCE) - foreach (arch ${_petsc_arches}) - if (NOT PETSC_ARCH) - find_path (petscconf petscconf.h + PATHS /usr/lib/petsc + # Debian paths + ${DEB_PATHS} + # Arch Linux path + /opt/petsc/linux-c-opt + # MacPorts path + /opt/local/lib/petsc + $ENV{HOME}/petsc + DOC "PETSc Directory" +) + +find_program(MAKE_EXECUTABLE NAMES make gmake) + +if(PETSC_DIR AND NOT PETSC_ARCH) + set(_petsc_arches + $ENV{PETSC_ARCH} # If set, use environment variable first + linux-gnu-c-debug linux-gnu-c-opt # Debian defaults + x86_64-unknown-linux-gnu i386-unknown-linux-gnu + ) + set(petscconf + "NOTFOUND" + CACHE FILEPATH "Cleared" FORCE + ) + foreach(arch ${_petsc_arches}) + if(NOT PETSC_ARCH) + find_path( + petscconf petscconf.h HINTS ${PETSC_DIR} PATH_SUFFIXES ${arch}/include bmake/${arch} - NO_DEFAULT_PATH) - if (petscconf) - set (PETSC_ARCH "${arch}" CACHE STRING "PETSc build architecture") - endif (petscconf) - endif (NOT PETSC_ARCH) - endforeach (arch) - set (petscconf "NOTFOUND" CACHE INTERNAL "Scratch variable" FORCE) -endif (PETSC_DIR AND NOT PETSC_ARCH) - -set (petsc_slaves LIBRARIES_SYS LIBRARIES_VEC LIBRARIES_MAT LIBRARIES_DM LIBRARIES_KSP LIBRARIES_SNES LIBRARIES_TS - INCLUDE_DIR INCLUDE_CONF) -include (FindPackageMultipass) -find_package_multipass (PETSc petsc_config_current - STATES DIR ARCH - DEPENDENTS INCLUDES LIBRARIES COMPILER MPIEXEC ${petsc_slaves}) + NO_DEFAULT_PATH + ) + if(petscconf) + set(PETSC_ARCH + "${arch}" + CACHE STRING "PETSc build architecture" + ) + endif(petscconf) + endif(NOT PETSC_ARCH) + endforeach(arch) + set(petscconf + "NOTFOUND" + CACHE INTERNAL "Scratch variable" FORCE + ) +endif(PETSC_DIR AND NOT PETSC_ARCH) + +set(petsc_slaves + LIBRARIES_SYS + LIBRARIES_VEC + LIBRARIES_MAT + LIBRARIES_DM + LIBRARIES_KSP + LIBRARIES_SNES + LIBRARIES_TS + INCLUDE_DIR + INCLUDE_CONF +) +include(FindPackageMultipass) +find_package_multipass( + PETSc + petsc_config_current + STATES + DIR + ARCH + DEPENDENTS + INCLUDES + LIBRARIES + COMPILER + MPIEXEC + ${petsc_slaves} +) # Determine whether the PETSc layout is old-style (through 2.3.3) or # new-style (>= 3.0.0) -if (EXISTS "${PETSC_DIR}/${PETSC_ARCH}/lib/petsc/conf/petscvariables") # > 3.5 - set (petsc_conf_rules "${PETSC_DIR}/lib/petsc/conf/rules") - set (petsc_conf_variables "${PETSC_DIR}/lib/petsc/conf/variables") -elseif (EXISTS "${PETSC_DIR}/${PETSC_ARCH}/include/petscconf.h") # > 2.3.3 - set (petsc_conf_rules "${PETSC_DIR}/conf/rules") - set (petsc_conf_variables "${PETSC_DIR}/conf/variables") -elseif (EXISTS "${PETSC_DIR}/bmake/${PETSC_ARCH}/petscconf.h") # <= 2.3.3 - set (petsc_conf_rules "${PETSC_DIR}/bmake/common/rules") - set (petsc_conf_variables "${PETSC_DIR}/bmake/common/variables") -elseif (PETSC_DIR) - message (SEND_ERROR "The pair PETSC_DIR=${PETSC_DIR} PETSC_ARCH=${PETSC_ARCH} do not specify a valid PETSc installation") -endif () - -if (petsc_conf_rules AND petsc_conf_variables AND NOT petsc_config_current) +if(EXISTS "${PETSC_DIR}/${PETSC_ARCH}/lib/petsc/conf/petscvariables") # > 3.5 + set(petsc_conf_rules "${PETSC_DIR}/lib/petsc/conf/rules") + set(petsc_conf_variables "${PETSC_DIR}/lib/petsc/conf/variables") +elseif(EXISTS "${PETSC_DIR}/${PETSC_ARCH}/include/petscconf.h") # > 2.3.3 + set(petsc_conf_rules "${PETSC_DIR}/conf/rules") + set(petsc_conf_variables "${PETSC_DIR}/conf/variables") +elseif(EXISTS "${PETSC_DIR}/bmake/${PETSC_ARCH}/petscconf.h") # <= 2.3.3 + set(petsc_conf_rules "${PETSC_DIR}/bmake/common/rules") + set(petsc_conf_variables "${PETSC_DIR}/bmake/common/variables") +elseif(PETSC_DIR) + message( + SEND_ERROR + "The pair PETSC_DIR=${PETSC_DIR} PETSC_ARCH=${PETSC_ARCH} do not specify a valid PETSc installation" + ) +endif() + +if(petsc_conf_rules + AND petsc_conf_variables + AND NOT petsc_config_current +) petsc_get_version() # Put variables into environment since they are needed to get # configuration (petscvariables) in the PETSc makefile - set (ENV{PETSC_DIR} "${PETSC_DIR}") - set (ENV{PETSC_ARCH} "${PETSC_ARCH}") + set(ENV{PETSC_DIR} "${PETSC_DIR}") + set(ENV{PETSC_ARCH} "${PETSC_ARCH}") # A temporary makefile to probe the PETSc configuration - set (petsc_config_makefile "${PROJECT_BINARY_DIR}/Makefile.petsc") - file (WRITE "${petsc_config_makefile}" -"## This file was autogenerated by FindPETSc.cmake + set(petsc_config_makefile "${PROJECT_BINARY_DIR}/Makefile.petsc") + file( + WRITE "${petsc_config_makefile}" + "## This file was autogenerated by FindPETSc.cmake # PETSC_DIR = ${PETSC_DIR} # PETSC_ARCH = ${PETSC_ARCH} include ${petsc_conf_rules} include ${petsc_conf_variables} show : \t-@echo -n \${\${VARIABLE}} -") - - macro (PETSC_GET_VARIABLE name var) - set (${var} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - execute_process (COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${petsc_config_makefile} show VARIABLE=${name} +" + ) + + macro(PETSC_GET_VARIABLE name var) + set(${var} + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) + execute_process( + COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f + ${petsc_config_makefile} show VARIABLE=${name} OUTPUT_VARIABLE ${var} - RESULT_VARIABLE petsc_return) - endmacro (PETSC_GET_VARIABLE) - petsc_get_variable (PETSC_LIB_DIR petsc_lib_dir) - petsc_get_variable (PETSC_EXTERNAL_LIB_BASIC petsc_libs_external) - petsc_get_variable (PETSC_CCPPFLAGS petsc_cpp_line) - petsc_get_variable (PETSC_INCLUDE petsc_include) - petsc_get_variable (PCC petsc_cc) - petsc_get_variable (PCC_FLAGS petsc_cc_flags) - petsc_get_variable (MPIEXEC petsc_mpiexec) + RESULT_VARIABLE petsc_return + ) + endmacro(PETSC_GET_VARIABLE) + petsc_get_variable(PETSC_LIB_DIR petsc_lib_dir) + petsc_get_variable(PETSC_EXTERNAL_LIB_BASIC petsc_libs_external) + petsc_get_variable(PETSC_CCPPFLAGS petsc_cpp_line) + petsc_get_variable(PETSC_INCLUDE petsc_include) + petsc_get_variable(PCC petsc_cc) + petsc_get_variable(PCC_FLAGS petsc_cc_flags) + petsc_get_variable(MPIEXEC petsc_mpiexec) # We are done with the temporary Makefile, calling PETSC_GET_VARIABLE after this point is invalid! - file (REMOVE ${petsc_config_makefile}) + file(REMOVE ${petsc_config_makefile}) - include (ResolveCompilerPaths) + include(ResolveCompilerPaths) # Extract include paths and libraries from compile command line - resolve_includes (petsc_includes_all "${petsc_cpp_line}") + resolve_includes(petsc_includes_all "${petsc_cpp_line}") #on windows we need to make sure we're linking against the right #runtime library - if (WIN32) - if (petsc_cc_flags MATCHES "-MT") + if(WIN32) + if(petsc_cc_flags MATCHES "-MT") set(using_md False) - foreach(flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + foreach( + flag_var + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + ) if(${flag_var} MATCHES "/MD") set(using_md True) endif(${flag_var} MATCHES "/MD") endforeach(flag_var) if(${using_md} MATCHES "True") - message(WARNING "PETSc was built with /MT, but /MD is currently set. - See http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F") + message( + WARNING + "PETSc was built with /MT, but /MD is currently set. + See http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F" + ) endif(${using_md} MATCHES "True") - endif (petsc_cc_flags MATCHES "-MT") - endif (WIN32) + endif(petsc_cc_flags MATCHES "-MT") + endif(WIN32) - include (CorrectWindowsPaths) + include(CorrectWindowsPaths) convert_cygwin_path(petsc_lib_dir) - message (STATUS "petsc_lib_dir ${petsc_lib_dir}") - - macro (PETSC_FIND_LIBRARY suffix name) - set (PETSC_LIBRARY_${suffix} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) # Clear any stale value, if we got here, we need to find it again - if (WIN32) - set (libname lib${name}) #windows expects "libfoo", linux expects "foo" - else (WIN32) - set (libname ${name}) - endif (WIN32) - find_library (PETSC_LIBRARY_${suffix} NAMES ${libname} HINTS ${petsc_lib_dir} NO_DEFAULT_PATH) - set (PETSC_LIBRARIES_${suffix} "${PETSC_LIBRARY_${suffix}}") - mark_as_advanced (PETSC_LIBRARY_${suffix}) - endmacro (PETSC_FIND_LIBRARY suffix name) + message(STATUS "petsc_lib_dir ${petsc_lib_dir}") + + macro(PETSC_FIND_LIBRARY suffix name) + set(PETSC_LIBRARY_${suffix} + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) # Clear any stale value, if we got here, we need to find it again + if(WIN32) + set(libname lib${name}) #windows expects "libfoo", linux expects "foo" + else(WIN32) + set(libname ${name}) + endif(WIN32) + find_library( + PETSC_LIBRARY_${suffix} + NAMES ${libname} + HINTS ${petsc_lib_dir} + NO_DEFAULT_PATH + ) + set(PETSC_LIBRARIES_${suffix} "${PETSC_LIBRARY_${suffix}}") + mark_as_advanced(PETSC_LIBRARY_${suffix}) + endmacro( + PETSC_FIND_LIBRARY + suffix + name + ) # Look for petscvec first, if it doesn't exist, we must be using single-library - petsc_find_library (VEC petscvec) - if (PETSC_LIBRARY_VEC) - petsc_find_library (SYS "petscsys;petsc") # libpetscsys is called libpetsc prior to 3.1 (when single-library was introduced) - petsc_find_library (MAT petscmat) - petsc_find_library (DM petscdm) - petsc_find_library (KSP petscksp) - petsc_find_library (SNES petscsnes) - petsc_find_library (TS petscts) - macro (PETSC_JOIN libs deps) - list (APPEND PETSC_LIBRARIES_${libs} ${PETSC_LIBRARIES_${deps}}) - endmacro (PETSC_JOIN libs deps) - petsc_join (VEC SYS) - petsc_join (MAT VEC) - petsc_join (DM MAT) - petsc_join (KSP DM) - petsc_join (SNES KSP) - petsc_join (TS SNES) - petsc_join (ALL TS) - else () - set (PETSC_LIBRARY_VEC "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) # There is no libpetscvec - petsc_find_library (SINGLE petsc) + petsc_find_library(VEC petscvec) + if(PETSC_LIBRARY_VEC) + petsc_find_library(SYS "petscsys;petsc") + # libpetscsys is called libpetsc prior to 3.1 (when single-library was introduced) + petsc_find_library(MAT petscmat) + petsc_find_library(DM petscdm) + petsc_find_library(KSP petscksp) + petsc_find_library(SNES petscsnes) + petsc_find_library(TS petscts) + macro(PETSC_JOIN libs deps) + list(APPEND PETSC_LIBRARIES_${libs} ${PETSC_LIBRARIES_${deps}}) + endmacro( + PETSC_JOIN + libs + deps + ) + petsc_join(VEC SYS) + petsc_join(MAT VEC) + petsc_join(DM MAT) + petsc_join(KSP DM) + petsc_join(SNES KSP) + petsc_join(TS SNES) + petsc_join(ALL TS) + else() + set(PETSC_LIBRARY_VEC + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) # There is no libpetscvec + petsc_find_library(SINGLE petsc) # Debian 9/Ubuntu 16.04 uses _real and _complex extensions when using libraries in /usr/lib/petsc. - if (NOT PETSC_LIBRARY_SINGLE) - petsc_find_library (SINGLE petsc_real) + if(NOT PETSC_LIBRARY_SINGLE) + petsc_find_library(SINGLE petsc_real) endif() - if (NOT PETSC_LIBRARY_SINGLE) - petsc_find_library (SINGLE petsc_complex) + if(NOT PETSC_LIBRARY_SINGLE) + petsc_find_library(SINGLE petsc_complex) endif() - foreach (pkg SYS VEC MAT DM KSP SNES TS ALL) - set (PETSC_LIBRARIES_${pkg} "${PETSC_LIBRARY_SINGLE}") - endforeach () - endif () - if (PETSC_LIBRARY_TS) - message (STATUS "Recognized PETSc install with separate libraries for each package") - else () - message (STATUS "Recognized PETSc install with single library for all packages") - endif () + foreach( + pkg + SYS + VEC + MAT + DM + KSP + SNES + TS + ALL + ) + set(PETSC_LIBRARIES_${pkg} "${PETSC_LIBRARY_SINGLE}") + endforeach() + endif() + if(PETSC_LIBRARY_TS) + message( + STATUS "Recognized PETSc install with separate libraries for each package" + ) + else() + message( + STATUS "Recognized PETSc install with single library for all packages" + ) + endif() include(Check${PETSC_LANGUAGE_BINDINGS}SourceRuns) - macro (petsc_test_compiles includes libraries runs) - message(STATUS "PETSc test with : ${includes} ${libraries}" ) - if (PETSC_VERSION VERSION_GREATER 3.1) - set (_PETSC_TSDestroy "TSDestroy(&ts)") - else () - set (_PETSC_TSDestroy "TSDestroy(ts)") - endif () + macro(petsc_test_compiles includes libraries runs) + message(STATUS "PETSc test with : ${includes} ${libraries}") + if(PETSC_VERSION VERSION_GREATER 3.1) + set(_PETSC_TSDestroy "TSDestroy(&ts)") + else() + set(_PETSC_TSDestroy "TSDestroy(ts)") + endif() - set(_PETSC_TEST_SOURCE " + set(_PETSC_TEST_SOURCE + " static const char help[] = \"PETSc test program.\"; #include int main(int argc,char *argv[]) { @@ -286,116 +396,197 @@ int main(int argc,char *argv[]) { ierr = PetscFinalize();CHKERRQ(ierr); return 0; } -") - multipass_source_compiles ("${includes}" "${libraries}" "${_PETSC_TEST_SOURCE}" ${runs} "${PETSC_LANGUAGE_BINDINGS}") - if (${${runs}}) - set (PETSC_EXECUTABLE_COMPILES "YES" CACHE BOOL - "Can the system successfully run a PETSc executable? This variable can be manually set to \"YES\" to force CMake to accept a given PETSc configuration, but this will almost always result in a broken build. If you change PETSC_DIR, PETSC_ARCH, or PETSC_CURRENT you would have to reset this variable." FORCE) - endif (${${runs}}) - endmacro () - +" + ) + multipass_source_compiles( + "${includes}" "${libraries}" "${_PETSC_TEST_SOURCE}" ${runs} + "${PETSC_LANGUAGE_BINDINGS}" + ) + if(${${runs}}) + set(PETSC_EXECUTABLE_COMPILES + "YES" + CACHE + BOOL + "Can the system successfully run a PETSc executable? This variable can be manually set to \"YES\" to force CMake to accept a given PETSc configuration, but this will almost always result in a broken build. If you change PETSC_DIR, PETSC_ARCH, or PETSC_CURRENT you would have to reset this variable." + FORCE + ) + endif(${${runs}}) + endmacro() - find_path (PETSC_INCLUDE_DIR petscts.h + find_path( + PETSC_INCLUDE_DIR petscts.h HINTS "${PETSC_DIR}" PATH_SUFFIXES include - NO_DEFAULT_PATH) - find_path (PETSC_INCLUDE_CONF petscconf.h + NO_DEFAULT_PATH + ) + find_path( + PETSC_INCLUDE_CONF petscconf.h HINTS "${PETSC_DIR}" PATH_SUFFIXES "${PETSC_ARCH}/include" "bmake/${PETSC_ARCH}" - NO_DEFAULT_PATH) - mark_as_advanced (PETSC_INCLUDE_DIR PETSC_INCLUDE_CONF) - set (petsc_includes_minimal ${PETSC_INCLUDE_CONF} ${PETSC_INCLUDE_DIR}) - - file (STRINGS "${PETSC_INCLUDE_CONF}/petscconf.h" PETSC_HAS_OPENMP REGEX "#define PETSC_HAVE_OPENMP 1") - if (PETSC_HAS_OPENMP) + NO_DEFAULT_PATH + ) + mark_as_advanced(PETSC_INCLUDE_DIR PETSC_INCLUDE_CONF) + set(petsc_includes_minimal ${PETSC_INCLUDE_CONF} ${PETSC_INCLUDE_DIR}) + + file(STRINGS "${PETSC_INCLUDE_CONF}/petscconf.h" PETSC_HAS_OPENMP + REGEX "#define PETSC_HAVE_OPENMP 1" + ) + if(PETSC_HAS_OPENMP) find_package(OpenMP REQUIRED) - set (petsc_openmp_library ";OpenMP::OpenMP_${PETSC_LANGUAGE_BINDINGS}") + set(petsc_openmp_library ";OpenMP::OpenMP_${PETSC_LANGUAGE_BINDINGS}") endif() - set (petsc_mpi_include_dirs "${MPI_${PETSC_LANGUAGE_BINDINGS}_INCLUDE_DIRS}") + set(petsc_mpi_include_dirs "${MPI_${PETSC_LANGUAGE_BINDINGS}_INCLUDE_DIRS}") #set (petsc_additional_libraries "MPI::MPI_${PETSC_LANGUAGE_BINDINGS}${petsc_openmp_library}") - petsc_test_compiles ("${petsc_includes_minimal};${petsc_mpi_include_dirs}" - "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" - petsc_works_minimal) - if (petsc_works_minimal) - message (STATUS "Minimal PETSc includes and libraries work. This probably means we are building with shared libs.") - set (petsc_includes_needed "${petsc_includes_minimal}") - else (petsc_works_minimal) # Minimal includes fail, see if just adding full includes fixes it - petsc_test_compiles ("${petsc_includes_all};${petsc_mpi_include_dirs}" + petsc_test_compiles( + "${petsc_includes_minimal};${petsc_mpi_include_dirs}" + "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" petsc_works_minimal + ) + if(petsc_works_minimal) + message( + STATUS + "Minimal PETSc includes and libraries work. This probably means we are building with shared libs." + ) + set(petsc_includes_needed "${petsc_includes_minimal}") + else(petsc_works_minimal + )# Minimal includes fail, see if just adding full includes fixes it + petsc_test_compiles( + "${petsc_includes_all};${petsc_mpi_include_dirs}" "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" - petsc_works_allincludes) - if (petsc_works_allincludes) # It does, we just need all the includes ( - message (STATUS "PETSc requires extra include paths, but links correctly with only interface libraries. This is an unexpected configuration (but it seems to work fine).") - set (petsc_includes_needed ${petsc_includes_all}) - else (petsc_works_allincludes) # We are going to need to link the external libs explicitly - resolve_libraries (petsc_libraries_external "${petsc_libs_external}") - foreach (pkg SYS VEC MAT DM KSP SNES TS ALL) - list (APPEND PETSC_LIBRARIES_${pkg} ${petsc_libraries_external}) - endforeach (pkg) - petsc_test_compiles ("${petsc_includes_minimal};${petsc_mpi_include_dirs}" + petsc_works_allincludes + ) + if(petsc_works_allincludes) # It does, we just need all the includes ( + message( + STATUS + "PETSc requires extra include paths, but links correctly with only interface libraries. This is an unexpected configuration (but it seems to work fine)." + ) + set(petsc_includes_needed ${petsc_includes_all}) + else(petsc_works_allincludes + )# We are going to need to link the external libs explicitly + resolve_libraries(petsc_libraries_external "${petsc_libs_external}") + foreach( + pkg + SYS + VEC + MAT + DM + KSP + SNES + TS + ALL + ) + list(APPEND PETSC_LIBRARIES_${pkg} ${petsc_libraries_external}) + endforeach(pkg) + petsc_test_compiles( + "${petsc_includes_minimal};${petsc_mpi_include_dirs}" "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" - petsc_works_alllibraries) - if (petsc_works_alllibraries) - message (STATUS "PETSc only need minimal includes, but requires explicit linking to all dependencies. This is expected when PETSc is built with static libraries.") - set (petsc_includes_needed ${petsc_includes_minimal}) - else (petsc_works_alllibraries) + petsc_works_alllibraries + ) + if(petsc_works_alllibraries) + message( + STATUS + "PETSc only need minimal includes, but requires explicit linking to all dependencies. This is expected when PETSc is built with static libraries." + ) + set(petsc_includes_needed ${petsc_includes_minimal}) + else(petsc_works_alllibraries) # It looks like we really need everything, should have listened to Matt - set (petsc_includes_needed ${petsc_includes_all}) - petsc_test_compiles ("${petsc_includes_all};${petsc_mpi_include_dirs}" - "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" - petsc_works_all) - if (petsc_works_all) # We fail anyways - message (STATUS "PETSc requires extra include paths and explicit linking to all dependencies. This probably means you have static libraries and something unexpected in PETSc headers.") - else (petsc_works_all) # We fail anyways - message (STATUS "PETSc could not be used, maybe the install is broken.") - endif (petsc_works_all) - endif (petsc_works_alllibraries) - endif (petsc_works_allincludes) - endif (petsc_works_minimal) + set(petsc_includes_needed ${petsc_includes_all}) + petsc_test_compiles( + "${petsc_includes_all};${petsc_mpi_include_dirs}" + "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" petsc_works_all + ) + if(petsc_works_all) # We fail anyways + message( + STATUS + "PETSc requires extra include paths and explicit linking to all dependencies. This probably means you have static libraries and something unexpected in PETSc headers." + ) + else(petsc_works_all) # We fail anyways + message( + STATUS "PETSc could not be used, maybe the install is broken." + ) + endif(petsc_works_all) + endif(petsc_works_alllibraries) + endif(petsc_works_allincludes) + endif(petsc_works_minimal) # We do an out-of-source build so __FILE__ will be an absolute path, hence __INSDIR__ is superfluous - if (${PETSC_VERSION} VERSION_LESS 3.1) - set (PETSC_DEFINITIONS "-D__SDIR__=\"\"" CACHE STRING "PETSc definitions" FORCE) - else () - set (PETSC_DEFINITIONS "-D__INSDIR__=" CACHE STRING "PETSc definitions" FORCE) - endif () + if(${PETSC_VERSION} VERSION_LESS 3.1) + set(PETSC_DEFINITIONS + "-D__SDIR__=\"\"" + CACHE STRING "PETSc definitions" FORCE + ) + else() + set(PETSC_DEFINITIONS + "-D__INSDIR__=" + CACHE STRING "PETSc definitions" FORCE + ) + endif() # Sometimes this can be used to assist FindMPI.cmake - set (PETSC_MPIEXEC ${petsc_mpiexec} CACHE FILEPATH "Executable for running PETSc MPI programs" FORCE) - set (PETSC_INCLUDES ${petsc_includes_needed} CACHE STRING "PETSc include path" FORCE) - set (PETSC_LIBRARIES ${PETSC_LIBRARIES_ALL} CACHE STRING "PETSc libraries" FORCE) - set (PETSC_COMPILER ${petsc_cc} CACHE FILEPATH "PETSc compiler" FORCE) -endif () + set(PETSC_MPIEXEC + ${petsc_mpiexec} + CACHE FILEPATH "Executable for running PETSc MPI programs" FORCE + ) + set(PETSC_INCLUDES + ${petsc_includes_needed} + CACHE STRING "PETSc include path" FORCE + ) + set(PETSC_LIBRARIES + ${PETSC_LIBRARIES_ALL} + CACHE STRING "PETSc libraries" FORCE + ) + set(PETSC_COMPILER + ${petsc_cc} + CACHE FILEPATH "PETSc compiler" FORCE + ) +endif() -if (NOT PETSC_INCLUDES AND NOT TARGET PETSc::PETSc) +if(NOT PETSC_INCLUDES AND NOT TARGET PETSc::PETSc) find_package(PkgConfig) - if (PkgConfig_FOUND) + if(PkgConfig_FOUND) pkg_search_module(PkgPETSC PETSc>3.4.0 petsc>3.4.0) - set (PETSC_LIBRARIES ${PkgPETSC_LINK_LIBRARIES} CACHE STRING "PETSc libraries" FORCE) - set (PETSC_INCLUDES ${PkgPETSC_INCLUDE_DIRS} CACHE STRING "PETSc include path" FORCE) - set (PETSC_EXECUTABLE_COMPILES "YES" CACHE BOOL - "Can the system successfully run a PETSc executable? This variable can be manually set to \"YES\" to force CMake to accept a given PETSc configuration, but this will almost always result in a broken build. If you change PETSC_DIR, PETSC_ARCH, or PETSC_CURRENT you would have to reset this variable." FORCE) + set(PETSC_LIBRARIES + ${PkgPETSC_LINK_LIBRARIES} + CACHE STRING "PETSc libraries" FORCE + ) + set(PETSC_INCLUDES + ${PkgPETSC_INCLUDE_DIRS} + CACHE STRING "PETSc include path" FORCE + ) + set(PETSC_EXECUTABLE_COMPILES + "YES" + CACHE + BOOL + "Can the system successfully run a PETSc executable? This variable can be manually set to \"YES\" to force CMake to accept a given PETSc configuration, but this will almost always result in a broken build. If you change PETSC_DIR, PETSC_ARCH, or PETSC_CURRENT you would have to reset this variable." + FORCE + ) endif() endif() # Note that we have forced values for all these choices. If you # change these, you are telling the system to trust you that they # work. It is likely that you will end up with a broken build. -mark_as_advanced (PETSC_INCLUDES PETSC_LIBRARIES PETSC_COMPILER PETSC_DEFINITIONS PETSC_MPIEXEC PETSC_EXECUTABLE_COMPILES) - -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args (PETSc +mark_as_advanced( + PETSC_INCLUDES PETSC_LIBRARIES PETSC_COMPILER PETSC_DEFINITIONS PETSC_MPIEXEC + PETSC_EXECUTABLE_COMPILES +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + PETSc REQUIRED_VARS PETSC_INCLUDES PETSC_LIBRARIES VERSION_VAR PETSC_VERSION - FAIL_MESSAGE "PETSc could not be found. Be sure to set PETSC_DIR and PETSC_ARCH.") + FAIL_MESSAGE + "PETSc could not be found. Be sure to set PETSC_DIR and PETSC_ARCH." +) -if (PETSC_FOUND) - if (NOT TARGET PETSc::PETSc) +if(PETSC_FOUND) + if(NOT TARGET PETSc::PETSc) add_library(PETSc::PETSc UNKNOWN IMPORTED) list(GET PETSC_LIBRARIES 0 PETSC_LIBRARY) target_link_libraries(PETSc::PETSc INTERFACE "${PETSC_LIBRARIES}") - set_target_properties(PETSc::PETSc PROPERTIES - IMPORTED_LOCATION "${PETSC_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${PETSC_INCLUDES}" - ) + set_target_properties( + PETSc::PETSc PROPERTIES IMPORTED_LOCATION "${PETSC_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${PETSC_INCLUDES}" + ) endif() endif() diff --git a/cmake/FindPackageMultipass.cmake b/cmake/FindPackageMultipass.cmake index 99bbace448..b2762f5d28 100644 --- a/cmake/FindPackageMultipass.cmake +++ b/cmake/FindPackageMultipass.cmake @@ -34,99 +34,124 @@ include(CheckCXXSourceCompiles) -macro (FIND_PACKAGE_MULTIPASS _name _current) - string (TOUPPER ${_name} _NAME) - set (_args ${ARGV}) - list (REMOVE_AT _args 0 1) +macro(FIND_PACKAGE_MULTIPASS _name _current) + string(TOUPPER ${_name} _NAME) + set(_args ${ARGV}) + list(REMOVE_AT _args 0 1) - set (_states_current "YES") - list (GET _args 0 _cmd) - if (_cmd STREQUAL "STATES") - list (REMOVE_AT _args 0) - list (GET _args 0 _state) - while (_state AND NOT _state STREQUAL "DEPENDENTS") + set(_states_current "YES") + list(GET _args 0 _cmd) + if(_cmd STREQUAL "STATES") + list(REMOVE_AT _args 0) + list(GET _args 0 _state) + while(_state AND NOT _state STREQUAL "DEPENDENTS") # The name of the stored value for the given state - set (_stored_var PACKAGE_MULTIPASS_${_NAME}_${_state}) - if (NOT "${${_stored_var}}" STREQUAL "${${_NAME}_${_state}}") - set (_states_current "NO") - endif (NOT "${${_stored_var}}" STREQUAL "${${_NAME}_${_state}}") - set (${_stored_var} "${${_NAME}_${_state}}" CACHE INTERNAL "Stored state for ${_name}." FORCE) - list (REMOVE_AT _args 0) - list (GET _args 0 _state) - endwhile (_state AND NOT _state STREQUAL "DEPENDENTS") - endif (_cmd STREQUAL "STATES") + set(_stored_var PACKAGE_MULTIPASS_${_NAME}_${_state}) + if(NOT "${${_stored_var}}" STREQUAL "${${_NAME}_${_state}}") + set(_states_current "NO") + endif(NOT "${${_stored_var}}" STREQUAL "${${_NAME}_${_state}}") + set(${_stored_var} + "${${_NAME}_${_state}}" + CACHE INTERNAL "Stored state for ${_name}." FORCE + ) + list(REMOVE_AT _args 0) + list(GET _args 0 _state) + endwhile(_state AND NOT _state STREQUAL "DEPENDENTS") + endif(_cmd STREQUAL "STATES") - set (_stored ${_NAME}_CURRENT) - if (NOT ${_stored}) - set (${_stored} "YES" CACHE BOOL "Is the configuration for ${_name} current? Set to \"NO\" to reconfigure." FORCE) - set (_states_current "NO") - endif (NOT ${_stored}) + set(_stored ${_NAME}_CURRENT) + if(NOT ${_stored}) + set(${_stored} + "YES" + CACHE + BOOL + "Is the configuration for ${_name} current? Set to \"NO\" to reconfigure." + FORCE + ) + set(_states_current "NO") + endif(NOT ${_stored}) - set (${_current} ${_states_current}) - if (NOT ${_current} AND PACKAGE_MULTIPASS_${_name}_CALLED) - message (STATUS "Clearing ${_name} dependent variables") + set(${_current} ${_states_current}) + if(NOT ${_current} AND PACKAGE_MULTIPASS_${_name}_CALLED) + message(STATUS "Clearing ${_name} dependent variables") # Clear all the dependent variables so that the module can reset them - list (GET _args 0 _cmd) - if (_cmd STREQUAL "DEPENDENTS") - list (REMOVE_AT _args 0) - foreach (dep ${_args}) - set (${_NAME}_${dep} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - endforeach (dep) - endif (_cmd STREQUAL "DEPENDENTS") - set (${_NAME}_FOUND "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - endif () - set (PACKAGE_MULTIPASS_${name}_CALLED YES CACHE INTERNAL "Private" FORCE) -endmacro (FIND_PACKAGE_MULTIPASS) - + list(GET _args 0 _cmd) + if(_cmd STREQUAL "DEPENDENTS") + list(REMOVE_AT _args 0) + foreach(dep ${_args}) + set(${_NAME}_${dep} + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) + endforeach(dep) + endif(_cmd STREQUAL "DEPENDENTS") + set(${_NAME}_FOUND + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) + endif() + set(PACKAGE_MULTIPASS_${name}_CALLED + YES + CACHE INTERNAL "Private" FORCE + ) +endmacro(FIND_PACKAGE_MULTIPASS) -macro (MULTIPASS_SOURCE_RUNS includes libraries source runs language) - include (Check${language}SourceRuns) +macro(MULTIPASS_SOURCE_RUNS includes libraries source runs language) + include(Check${language}SourceRuns) # This is a ridiculous hack. CHECK_${language}_SOURCE_* thinks that if the # *name* of the return variable doesn't change, then the test does # not need to be re-run. We keep an internal count which we # increment to guarantee that every test name is unique. If we've # gotten here, then the configuration has changed enough that the # test *needs* to be rerun. - if (NOT MULTIPASS_TEST_COUNT) - set (MULTIPASS_TEST_COUNT 00) - endif (NOT MULTIPASS_TEST_COUNT) - math (EXPR _tmp "${MULTIPASS_TEST_COUNT} + 1") # Why can't I add to a cache variable? - set (MULTIPASS_TEST_COUNT ${_tmp} CACHE INTERNAL "Unique test ID") - set (testname MULTIPASS_TEST_${MULTIPASS_TEST_COUNT}_${runs}) - set (CMAKE_REQUIRED_INCLUDES ${includes}) - set (CMAKE_REQUIRED_LIBRARIES ${libraries}) + if(NOT MULTIPASS_TEST_COUNT) + set(MULTIPASS_TEST_COUNT 00) + endif(NOT MULTIPASS_TEST_COUNT) + math(EXPR _tmp "${MULTIPASS_TEST_COUNT} + 1" + )# Why can't I add to a cache variable? + set(MULTIPASS_TEST_COUNT + ${_tmp} + CACHE INTERNAL "Unique test ID" + ) + set(testname MULTIPASS_TEST_${MULTIPASS_TEST_COUNT}_${runs}) + set(CMAKE_REQUIRED_INCLUDES ${includes}) + set(CMAKE_REQUIRED_LIBRARIES ${libraries}) if(${language} STREQUAL "C") - check_c_source_runs ("${source}" ${testname}) + check_c_source_runs("${source}" ${testname}) elseif(${language} STREQUAL "CXX") - check_cxx_source_runs ("${source}" ${testname}) + check_cxx_source_runs("${source}" ${testname}) endif() - set (${runs} "${${testname}}") -endmacro (MULTIPASS_SOURCE_RUNS) + set(${runs} "${${testname}}") +endmacro(MULTIPASS_SOURCE_RUNS) -macro (MULTIPASS_C_SOURCE_RUNS includes libraries source runs) +macro(MULTIPASS_C_SOURCE_RUNS includes libraries source runs) multipass_source_runs("${includes}" "${libraries}" "${source}" ${runs} "C") -endmacro (MULTIPASS_C_SOURCE_RUNS) +endmacro(MULTIPASS_C_SOURCE_RUNS) -macro (MULTIPASS_SOURCE_COMPILES includes libraries source runs language) - include (Check${language}SourceCompiles) +macro(MULTIPASS_SOURCE_COMPILES includes libraries source runs language) + include(Check${language}SourceCompiles) # This is a ridiculous hack. CHECK_${language}_SOURCE_* thinks that if the # *name* of the return variable doesn't change, then the test does # not need to be re-run. We keep an internal count which we # increment to guarantee that every test name is unique. If we've # gotten here, then the configuration has changed enough that the # test *needs* to be rerun. - if (NOT MULTIPASS_TEST_COUNT) - set (MULTIPASS_TEST_COUNT 00) - endif (NOT MULTIPASS_TEST_COUNT) - math (EXPR _tmp "${MULTIPASS_TEST_COUNT} + 1") # Why can't I add to a cache variable? - set (MULTIPASS_TEST_COUNT ${_tmp} CACHE INTERNAL "Unique test ID") - set (testname MULTIPASS_TEST_${MULTIPASS_TEST_COUNT}_${runs}) - set (CMAKE_REQUIRED_INCLUDES ${includes}) - set (CMAKE_REQUIRED_LIBRARIES ${libraries}) + if(NOT MULTIPASS_TEST_COUNT) + set(MULTIPASS_TEST_COUNT 00) + endif(NOT MULTIPASS_TEST_COUNT) + math(EXPR _tmp "${MULTIPASS_TEST_COUNT} + 1" + )# Why can't I add to a cache variable? + set(MULTIPASS_TEST_COUNT + ${_tmp} + CACHE INTERNAL "Unique test ID" + ) + set(testname MULTIPASS_TEST_${MULTIPASS_TEST_COUNT}_${runs}) + set(CMAKE_REQUIRED_INCLUDES ${includes}) + set(CMAKE_REQUIRED_LIBRARIES ${libraries}) if(${language} STREQUAL "C") - check_c_source_compiles ("${source}" ${testname}) + check_c_source_compiles("${source}" ${testname}) elseif(${language} STREQUAL "CXX") - check_cxx_source_compiles ("${source}" ${testname}) + check_cxx_source_compiles("${source}" ${testname}) endif() - set (${runs} "${${testname}}") -endmacro () + set(${runs} "${${testname}}") +endmacro() diff --git a/cmake/FindSLEPc.cmake b/cmake/FindSLEPc.cmake index 3add8eba8b..4df855569b 100644 --- a/cmake/FindSLEPc.cmake +++ b/cmake/FindSLEPc.cmake @@ -48,74 +48,92 @@ find_package(PETSc REQUIRED) find_package(MPI REQUIRED) # Set debian_arches (PETSC_ARCH for Debian-style installations) -foreach (debian_arches linux kfreebsd) - if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(DEBIAN_FLAVORS ${debian_arches}-gnu-c-debug ${debian_arches}-gnu-c-opt ${DEBIAN_FLAVORS}) +foreach(debian_arches linux kfreebsd) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set(DEBIAN_FLAVORS ${debian_arches}-gnu-c-debug ${debian_arches}-gnu-c-opt + ${DEBIAN_FLAVORS} + ) else() - set(DEBIAN_FLAVORS ${debian_arches}-gnu-c-opt ${debian_arches}-gnu-c-debug ${DEBIAN_FLAVORS}) + set(DEBIAN_FLAVORS ${debian_arches}-gnu-c-opt ${debian_arches}-gnu-c-debug + ${DEBIAN_FLAVORS} + ) endif() endforeach() # List of possible locations for SLEPC_DIR set(slepc_dir_locations "") list(APPEND slepc_dir_locations "/usr/lib/slepc") -list(APPEND slepc_dir_locations "/opt/local/lib/petsc") # Macports +list(APPEND slepc_dir_locations "/opt/local/lib/petsc") # Macports list(APPEND slepc_dir_locations "/usr/local/lib/slepc") list(APPEND slepc_dir_locations "$ENV{HOME}/slepc") # Try to figure out SLEPC_DIR by finding slepc.h -find_path(SLEPC_DIR include/slepc.h +find_path( + SLEPC_DIR include/slepc.h HINTS ${SLEPC_DIR} $ENV{SLEPC_DIR} PATHS ${slepc_dir_locations} - DOC "SLEPc directory") + DOC "SLEPc directory" +) # Report result of search for SLEPC_DIR -if (DEFINED SLEPC_DIR) +if(DEFINED SLEPC_DIR) message(STATUS "SLEPC_DIR is ${SLEPC_DIR}") else() message(STATUS "SLEPC_DIR is empty") endif() # Get variables from SLEPc configuration -if (SLEPC_DIR) +if(SLEPC_DIR) - find_library(SLEPC_LIBRARY + find_library( + SLEPC_LIBRARY NAMES slepc - HINTS - ${SLEPC_DIR}/lib - $ENV{SLEPC_DIR}/lib - ${SLEPC_DIR}/${PETSC_ARCH}/lib - $ENV{SLEPC_DIR}/$ENV{PETSC_ARCH}/lib + HINTS ${SLEPC_DIR}/lib $ENV{SLEPC_DIR}/lib ${SLEPC_DIR}/${PETSC_ARCH}/lib + $ENV{SLEPC_DIR}/$ENV{PETSC_ARCH}/lib NO_DEFAULT_PATH - DOC "The SLEPc library") - find_library(SLEPC_LIBRARY + DOC "The SLEPc library" + ) + find_library( + SLEPC_LIBRARY NAMES slepc - DOC "The SLEPc library") + DOC "The SLEPc library" + ) mark_as_advanced(SLEPC_LIBRARY) # Find SLEPc config file - find_file(SLEPC_CONFIG_FILE NAMES slepc_common PATHS - ${SLEPC_DIR}/lib/slepc/conf - ${SLEPC_DIR}/lib/slepc-conf ${SLEPC_DIR}/conf) + find_file( + SLEPC_CONFIG_FILE + NAMES slepc_common + PATHS ${SLEPC_DIR}/lib/slepc/conf ${SLEPC_DIR}/lib/slepc-conf + ${SLEPC_DIR}/conf + ) # Create a temporary Makefile to probe the SLEPc configuration set(slepc_config_makefile ${PROJECT_BINARY_DIR}/Makefile.slepc) - file(WRITE ${slepc_config_makefile} -"# This file was autogenerated by FindSLEPc.cmake + file( + WRITE ${slepc_config_makefile} + "# This file was autogenerated by FindSLEPc.cmake SLEPC_DIR = ${SLEPC_DIR} PETSC_ARCH = ${PETSC_ARCH} PETSC_DIR = ${PETSC_DIR} include ${SLEPC_CONFIG_FILE} show : -@echo -n \${\${VARIABLE}} -") +" + ) # Define macro for getting SLEPc variables from Makefile macro(SLEPC_GET_VARIABLE var name) - set(${var} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - execute_process(COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${slepc_config_makefile} show VARIABLE=${name} + set(${var} + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) + execute_process( + COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f + ${slepc_config_makefile} show VARIABLE=${name} OUTPUT_VARIABLE ${var} - RESULT_VARIABLE slepc_return) + RESULT_VARIABLE slepc_return + ) endmacro() # Call macro to get the SLEPc variables @@ -131,15 +149,21 @@ show : resolve_libraries(SLEPC_EXTERNAL_LIBRARIES "${SLEPC_EXTERNAL_LIB}") # Add variables to CMake cache and mark as advanced - set(SLEPC_INCLUDE_DIRS ${SLEPC_INCLUDE_DIRS} CACHE STRING "SLEPc include paths." FORCE) - set(SLEPC_LIBRARIES ${SLEPC_LIBRARY} CACHE STRING "SLEPc libraries." FORCE) + set(SLEPC_INCLUDE_DIRS + ${SLEPC_INCLUDE_DIRS} + CACHE STRING "SLEPc include paths." FORCE + ) + set(SLEPC_LIBRARIES + ${SLEPC_LIBRARY} + CACHE STRING "SLEPc libraries." FORCE + ) mark_as_advanced(SLEPC_INCLUDE_DIRS SLEPC_LIBRARIES) endif() -if (SLEPC_SKIP_BUILD_TESTS) +if(SLEPC_SKIP_BUILD_TESTS) set(SLEPC_VERSION "UNKNOWN") set(SLEPC_VERSION_OK TRUE) -elseif (SLEPC_LIBRARIES AND SLEPC_INCLUDE_DIRS) +elseif(SLEPC_LIBRARIES AND SLEPC_INCLUDE_DIRS) # Set flags for building test program set(CMAKE_REQUIRED_INCLUDES ${SLEPC_INCLUDE_DIRS}) @@ -147,8 +171,11 @@ elseif (SLEPC_LIBRARIES AND SLEPC_INCLUDE_DIRS) # Check SLEPc version set(SLEPC_CONFIG_TEST_VERSION_CPP - "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/slepc_config_test_version.cpp") - file(WRITE ${SLEPC_CONFIG_TEST_VERSION_CPP} " + "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/slepc_config_test_version.cpp" + ) + file( + WRITE ${SLEPC_CONFIG_TEST_VERSION_CPP} + " #include #include \"slepcversion.h\" @@ -158,56 +185,69 @@ int main() { << SLEPC_VERSION_SUBMINOR; return 0; } -") +" + ) try_run( - SLEPC_CONFIG_TEST_VERSION_EXITCODE - SLEPC_CONFIG_TEST_VERSION_COMPILED - ${CMAKE_CURRENT_BINARY_DIR} - ${SLEPC_CONFIG_TEST_VERSION_CPP} - CMAKE_FLAGS - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}" + SLEPC_CONFIG_TEST_VERSION_EXITCODE SLEPC_CONFIG_TEST_VERSION_COMPILED + ${CMAKE_CURRENT_BINARY_DIR} ${SLEPC_CONFIG_TEST_VERSION_CPP} + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}" COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT RUN_OUTPUT_VARIABLE OUTPUT - ) + ) - if (SLEPC_CONFIG_TEST_VERSION_EXITCODE EQUAL 0) - set(SLEPC_VERSION "${OUTPUT}" CACHE STRING "SLEPC version number") + if(SLEPC_CONFIG_TEST_VERSION_EXITCODE EQUAL 0) + set(SLEPC_VERSION + "${OUTPUT}" + CACHE STRING "SLEPC version number" + ) string(REPLACE "." ";" SLEPC_VERSION_LIST ${SLEPC_VERSION}) list(GET SLEPC_VERSION_LIST 0 SLEPC_VERSION_MAJOR) list(GET SLEPC_VERSION_LIST 1 SLEPC_VERSION_MINOR) list(GET SLEPC_VERSION_LIST 2 SLEPC_VERSION_SUBMINOR) mark_as_advanced(SLEPC_VERSION) - mark_as_advanced(SLEPC_VERSION_MAJOR SLEPC_VERSION_MINOR SLEPC_VERSION_SUBMINOR) + mark_as_advanced( + SLEPC_VERSION_MAJOR SLEPC_VERSION_MINOR SLEPC_VERSION_SUBMINOR + ) endif() - if (SLEPc_FIND_VERSION) + if(SLEPc_FIND_VERSION) # Check if version found is >= required version - if (NOT "${SLEPC_VERSION}" VERSION_LESS "${SLEPc_FIND_VERSION}") - set(SLEPC_VERSION_OK TRUE CACHE BOOL "") + if(NOT "${SLEPC_VERSION}" VERSION_LESS "${SLEPc_FIND_VERSION}") + set(SLEPC_VERSION_OK + TRUE + CACHE BOOL "" + ) endif() else() # No specific version requested - set(SLEPC_VERSION_OK TRUE CACHE BOOL "") + set(SLEPC_VERSION_OK + TRUE + CACHE BOOL "" + ) endif() mark_as_advanced(SLEPC_VERSION_OK) endif() # Standard package handling include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SLEPc +find_package_handle_standard_args( + SLEPc FOUND_VAR SLEPC_FOUND - FAIL_MESSAGE "SLEPc could not be found. Be sure to set SLEPC_DIR, PETSC_DIR, and PETSC_ARCH." + FAIL_MESSAGE + "SLEPc could not be found. Be sure to set SLEPC_DIR, PETSC_DIR, and PETSC_ARCH." VERSION_VAR SLEPC_VERSION - REQUIRED_VARS SLEPC_LIBRARIES SLEPC_DIR SLEPC_INCLUDE_DIRS SLEPC_VERSION_OK) + REQUIRED_VARS SLEPC_LIBRARIES SLEPC_DIR SLEPC_INCLUDE_DIRS SLEPC_VERSION_OK +) -if (SLEPC_FOUND) - if (NOT TARGET SLEPc::SLEPc) +if(SLEPC_FOUND) + if(NOT TARGET SLEPc::SLEPc) add_library(SLEPc::SLEPc UNKNOWN IMPORTED) - set_target_properties(SLEPc::SLEPc PROPERTIES - IMPORTED_LOCATION "${SLEPC_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${SLEPC_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES PETSc::PETSc - ) + set_target_properties( + SLEPc::SLEPc + PROPERTIES IMPORTED_LOCATION "${SLEPC_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${SLEPC_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES PETSc::PETSc + ) endif() endif() diff --git a/cmake/FindSUNDIALS.cmake b/cmake/FindSUNDIALS.cmake index 15b266d06a..e5cec34510 100644 --- a/cmake/FindSUNDIALS.cmake +++ b/cmake/FindSUNDIALS.cmake @@ -33,47 +33,48 @@ include(FindPackageHandleStandardArgs) find_package(SUNDIALS CONFIG QUIET) -if (SUNDIALS_FOUND) - if (TARGET SUNDIALS::nvecparallel) +if(SUNDIALS_FOUND) + if(TARGET SUNDIALS::nvecparallel) return() else() message(STATUS "SUNDIALS found but not SUNDIALS::nvecparallel") endif() endif() -find_path(SUNDIALS_INCLUDE_DIR - sundials_config.h - HINTS - "${SUNDIALS_ROOT}" - ENV SUNDIALS_DIR +find_path( + SUNDIALS_INCLUDE_DIR sundials_config.h + HINTS "${SUNDIALS_ROOT}" ENV SUNDIALS_DIR PATH_SUFFIXES include include/sundials - DOC "SUNDIALS Directory") + DOC "SUNDIALS Directory" +) -if (SUNDIALS_DEBUG) +if(SUNDIALS_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " SUNDIALS_INCLUDE_DIR = ${SUNDIALS_INCLUDE_DIR}" - " SUNDIALS_ROOT = ${SUNDIALS_ROOT}") + " SUNDIALS_INCLUDE_DIR = ${SUNDIALS_INCLUDE_DIR}" + " SUNDIALS_ROOT = ${SUNDIALS_ROOT}" + ) endif() set(SUNDIALS_INCLUDE_DIRS - "${SUNDIALS_INCLUDE_DIR}" - "${SUNDIALS_INCLUDE_DIR}/.." - CACHE STRING "SUNDIALS include directories") + "${SUNDIALS_INCLUDE_DIR}" "${SUNDIALS_INCLUDE_DIR}/.." + CACHE STRING "SUNDIALS include directories" +) -find_library(SUNDIALS_nvecparallel_LIBRARY +find_library( + SUNDIALS_nvecparallel_LIBRARY NAMES sundials_nvecparallel - HINTS - "${SUNDIALS_INCLUDE_DIR}/.." - "${SUNDIALS_INCLUDE_DIR}/../.." + HINTS "${SUNDIALS_INCLUDE_DIR}/.." "${SUNDIALS_INCLUDE_DIR}/../.." PATH_SUFFIXES lib lib64 - ) +) -if (SUNDIALS_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " SUNDIALS_nvecparallel_LIBRARY = ${SUNDIALS_nvecparallel_LIBRARY}") +if(SUNDIALS_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " SUNDIALS_nvecparallel_LIBRARY = ${SUNDIALS_nvecparallel_LIBRARY}" + ) endif() -if (NOT SUNDIALS_nvecparallel_LIBRARY) +if(NOT SUNDIALS_nvecparallel_LIBRARY) message(FATAL_ERROR "Sundials requested but SUNDIALS nvecparallel not found.") endif() list(APPEND SUNDIALS_LIBRARIES "${SUNDIALS_nvecparallel_LIBRARY}") @@ -81,62 +82,86 @@ mark_as_advanced(SUNDIALS_nvecparallel_LIBRARY) set(SUNDIALS_COMPONENTS arkode cvode ida) -foreach (LIB ${SUNDIALS_COMPONENTS}) - find_library(SUNDIALS_${LIB}_LIBRARY +foreach(LIB ${SUNDIALS_COMPONENTS}) + find_library( + SUNDIALS_${LIB}_LIBRARY NAMES sundials_${LIB} - HINTS - "${SUNDIALS_INCLUDE_DIR}/.." - "${SUNDIALS_INCLUDE_DIR}/../.." + HINTS "${SUNDIALS_INCLUDE_DIR}/.." "${SUNDIALS_INCLUDE_DIR}/../.." PATH_SUFFIXES lib lib64 - ) + ) - if (SUNDIALS_DEBUG) + if(SUNDIALS_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " SUNDIALS_${LIB}_LIBRARY = ${SUNDIALS_${LIB}_LIBRARY}") + " SUNDIALS_${LIB}_LIBRARY = ${SUNDIALS_${LIB}_LIBRARY}" + ) endif() - if (NOT SUNDIALS_${LIB}_LIBRARY) + if(NOT SUNDIALS_${LIB}_LIBRARY) message(FATAL_ERROR "Sundials requested but SUNDIALS ${LIB} not found.") endif() list(APPEND SUNDIALS_LIBRARIES "${SUNDIALS_${LIB}_LIBRARY}") mark_as_advanced(SUNDIALS_${LIB}_LIBRARY) endforeach() -if (SUNDIALS_INCLUDE_DIR) +if(SUNDIALS_INCLUDE_DIR) file(READ "${SUNDIALS_INCLUDE_DIR}/sundials_config.h" SUNDIALS_CONFIG_FILE) set(SUNDIALS_VERSION_REGEX_PATTERN - ".*#define SUNDIALS_VERSION \"([0-9]+)\\.([0-9]+)\\.([0-9]+)\".*") - string(REGEX MATCH ${SUNDIALS_VERSION_REGEX_PATTERN} _ "${SUNDIALS_CONFIG_FILE}") - set(SUNDIALS_VERSION_MAJOR ${CMAKE_MATCH_1} CACHE STRING "") - set(SUNDIALS_VERSION_MINOR ${CMAKE_MATCH_2} CACHE STRING "") - set(SUNDIALS_VERSION_PATCH ${CMAKE_MATCH_3} CACHE STRING "") - set(SUNDIALS_VERSION "${SUNDIALS_VERSION_MAJOR}.${SUNDIALS_VERSION_MINOR}.${SUNDIALS_VERSION_PATCH}" CACHE STRING "SUNDIALS version") + ".*#define SUNDIALS_VERSION \"([0-9]+)\\.([0-9]+)\\.([0-9]+)\".*" + ) + string(REGEX MATCH ${SUNDIALS_VERSION_REGEX_PATTERN} _ + "${SUNDIALS_CONFIG_FILE}" + ) + set(SUNDIALS_VERSION_MAJOR + ${CMAKE_MATCH_1} + CACHE STRING "" + ) + set(SUNDIALS_VERSION_MINOR + ${CMAKE_MATCH_2} + CACHE STRING "" + ) + set(SUNDIALS_VERSION_PATCH + ${CMAKE_MATCH_3} + CACHE STRING "" + ) + set(SUNDIALS_VERSION + "${SUNDIALS_VERSION_MAJOR}.${SUNDIALS_VERSION_MINOR}.${SUNDIALS_VERSION_PATCH}" + CACHE STRING "SUNDIALS version" + ) endif() -if (SUNDIALS_DEBUG) +if(SUNDIALS_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " SUNDIALS_VERSION = ${SUNDIALS_VERSION}") + " SUNDIALS_VERSION = ${SUNDIALS_VERSION}" + ) endif() -find_package_handle_standard_args(SUNDIALS +find_package_handle_standard_args( + SUNDIALS REQUIRED_VARS SUNDIALS_LIBRARIES SUNDIALS_INCLUDE_DIR SUNDIALS_INCLUDE_DIRS VERSION_VAR SUNDIALS_VERSION - ) +) -set(SUNDIALS_LIBRARIES "${SUNDIALS_LIBRARIES}" CACHE STRING "SUNDIALS libraries") +set(SUNDIALS_LIBRARIES + "${SUNDIALS_LIBRARIES}" + CACHE STRING "SUNDIALS libraries" +) mark_as_advanced(SUNDIALS_LIBRARIES SUNDIALS_INCLUDE_DIR SUNDIALS_INCLUDE_DIRS) -if (SUNDIALS_FOUND AND NOT TARGET SUNDIALS::SUNDIALS) +if(SUNDIALS_FOUND AND NOT TARGET SUNDIALS::SUNDIALS) add_library(SUNDIALS::nvecparallel UNKNOWN IMPORTED) - set_target_properties(SUNDIALS::nvecparallel PROPERTIES - IMPORTED_LOCATION "${SUNDIALS_nvecparallel_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SUNDIALS_INCLUDE_DIRS}") + set_target_properties( + SUNDIALS::nvecparallel + PROPERTIES IMPORTED_LOCATION "${SUNDIALS_nvecparallel_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SUNDIALS_INCLUDE_DIRS}" + ) - foreach (LIB ${SUNDIALS_COMPONENTS}) + foreach(LIB ${SUNDIALS_COMPONENTS}) add_library(SUNDIALS::${LIB} UNKNOWN IMPORTED) - set_target_properties(SUNDIALS::${LIB} PROPERTIES - IMPORTED_LOCATION "${SUNDIALS_${LIB}_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SUNDIALS_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES SUNDIALS::nvecparallel) + set_target_properties( + SUNDIALS::${LIB} + PROPERTIES IMPORTED_LOCATION "${SUNDIALS_${LIB}_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SUNDIALS_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES SUNDIALS::nvecparallel + ) endforeach() endif() diff --git a/cmake/FindScoreP.cmake b/cmake/FindScoreP.cmake index dd119545e8..80fe102749 100644 --- a/cmake/FindScoreP.cmake +++ b/cmake/FindScoreP.cmake @@ -61,76 +61,90 @@ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - find_program(ScoreP_CONFIG scorep-config) mark_as_advanced(ScoreP_CONFIG) get_filename_component(ScoreP_TMP "${ScoreP_CONFIG}" DIRECTORY) get_filename_component(ScoreP_EXEC_LOCATION "${ScoreP_TMP}" DIRECTORY) -if (ScoreP_DEBUG) +if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_CONFIG = ${ScoreP_CONFIG}" - " ScoreP_EXEC_LOCATION = ${ScoreP_EXEC_LOCATION}") + " ScoreP_CONFIG = ${ScoreP_CONFIG}" + " ScoreP_EXEC_LOCATION = ${ScoreP_EXEC_LOCATION}" + ) endif() if(ScoreP_CONFIG) message(STATUS "SCOREP library found. (using ${ScoreP_CONFIG})") - execute_process(COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--cppflags" - OUTPUT_VARIABLE ScoreP_CONFIG_FLAGS) + execute_process( + COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--cppflags" + OUTPUT_VARIABLE ScoreP_CONFIG_FLAGS + ) - string(REGEX MATCHALL "-I[^ ]*" ScoreP_CONFIG_INCLUDES "${ScoreP_CONFIG_FLAGS}") + string(REGEX MATCHALL "-I[^ ]*" ScoreP_CONFIG_INCLUDES + "${ScoreP_CONFIG_FLAGS}" + ) foreach(inc ${ScoreP_CONFIG_INCLUDES}) string(SUBSTRING ${inc} 2 -1 inc) list(APPEND ScoreP_INCLUDE_DIRS ${inc}) endforeach() - if (ScoreP_DEBUG) + if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_INCLUDE_DIRS = ${ScoreP_INCLUDE_DIRS}") + " ScoreP_INCLUDE_DIRS = ${ScoreP_INCLUDE_DIRS}" + ) endif() - string(REGEX MATCHALL "(^| +)-[^I][^ ]*" ScoreP_CONFIG_CXXFLAGS "${ScoreP_CONFIG_FLAGS}") + string(REGEX MATCHALL "(^| +)-[^I][^ ]*" ScoreP_CONFIG_CXXFLAGS + "${ScoreP_CONFIG_FLAGS}" + ) foreach(flag ${ScoreP_CONFIG_CXXFLAGS}) string(STRIP ${flag} flag) list(APPEND ScoreP_CXX_FLAGS ${flag}) endforeach() - if (ScoreP_DEBUG) + if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_CXX_FLAGS = ${ScoreP_CXX_FLAGS}") + " ScoreP_CXX_FLAGS = ${ScoreP_CXX_FLAGS}" + ) endif() unset(ScoreP_CONFIG_FLAGS) unset(ScoreP_CONFIG_INCLUDES) unset(ScoreP_CONFIG_CXXFLAGS) - execute_process(COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--ldflags" - OUTPUT_VARIABLE _LINK_LD_ARGS) - string( REPLACE " " ";" _LINK_LD_ARGS ${_LINK_LD_ARGS} ) - foreach( _ARG ${_LINK_LD_ARGS} ) + execute_process( + COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--ldflags" + OUTPUT_VARIABLE _LINK_LD_ARGS + ) + string(REPLACE " " ";" _LINK_LD_ARGS ${_LINK_LD_ARGS}) + foreach(_ARG ${_LINK_LD_ARGS}) if(${_ARG} MATCHES "^-L") - STRING(REGEX REPLACE "^-L" "" _ARG ${_ARG}) - SET(ScoreP_LINK_DIRS ${ScoreP_LINK_DIRS} ${_ARG}) + string(REGEX REPLACE "^-L" "" _ARG ${_ARG}) + set(ScoreP_LINK_DIRS ${ScoreP_LINK_DIRS} ${_ARG}) endif() endforeach() - if (ScoreP_DEBUG) + if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_LINK_DIRS = ${ScoreP_LINK_DIRS}") + " ScoreP_LINK_DIRS = ${ScoreP_LINK_DIRS}" + ) endif() - execute_process(COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--libs" - OUTPUT_VARIABLE _LINK_LD_ARGS) - string( REPLACE " " ";" _LINK_LD_ARGS ${_LINK_LD_ARGS} ) - foreach( _ARG ${_LINK_LD_ARGS} ) + execute_process( + COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--libs" + OUTPUT_VARIABLE _LINK_LD_ARGS + ) + string(REPLACE " " ";" _LINK_LD_ARGS ${_LINK_LD_ARGS}) + foreach(_ARG ${_LINK_LD_ARGS}) if(${_ARG} MATCHES "^-l") string(REGEX REPLACE "^-l" "" _ARG ${_ARG}) - find_library(_SCOREP_LIB_FROM_ARG NAMES ${_ARG} - PATHS - ${ScoreP_LINK_DIRS} - ) + find_library( + _SCOREP_LIB_FROM_ARG + NAMES ${_ARG} + PATHS ${ScoreP_LINK_DIRS} + ) if(_SCOREP_LIB_FROM_ARG) set(ScoreP_LIBRARIES ${ScoreP_LIBRARIES} ${_SCOREP_LIB_FROM_ARG}) endif() @@ -138,26 +152,26 @@ if(ScoreP_CONFIG) endif() endforeach() - if (ScoreP_DEBUG) + if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_LIBRARIES = ${ScoreP_LIBRARIES}") + " ScoreP_LIBRARIES = ${ScoreP_LIBRARIES}" + ) endif() endif() -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args(ScoreP DEFAULT_MSG - ScoreP_CONFIG - ScoreP_LIBRARIES - ScoreP_INCLUDE_DIRS - ) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + ScoreP DEFAULT_MSG ScoreP_CONFIG ScoreP_LIBRARIES ScoreP_INCLUDE_DIRS +) -if (ScoreP_FOUND AND NOT TARGET ScoreP::ScoreP) +if(ScoreP_FOUND AND NOT TARGET ScoreP::ScoreP) add_library(ScoreP::ScoreP UNKNOWN IMPORTED) - set_target_properties(ScoreP::ScoreP PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${ScoreP_INCLUDE_DIRS}" - IMPORTED_LINK_INTERFACE_LIBRARIES "${ScoreP_LIBRARIES}" - INTERFACE_INCLUDE_DEFINITIONS "${ScoreP_CXX_FLAGS}" - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - ) + set_target_properties( + ScoreP::ScoreP + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ScoreP_INCLUDE_DIRS}" + IMPORTED_LINK_INTERFACE_LIBRARIES "${ScoreP_LIBRARIES}" + INTERFACE_INCLUDE_DEFINITIONS "${ScoreP_CXX_FLAGS}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + ) endif() diff --git a/cmake/FindSphinx.cmake b/cmake/FindSphinx.cmake index fd377d0d00..a15ae8d6f9 100644 --- a/cmake/FindSphinx.cmake +++ b/cmake/FindSphinx.cmake @@ -14,13 +14,15 @@ # https://devblogs.microsoft.com/cppblog/clear-functional-c-documentation-with-sphinx-breathe-doxygen-cmake/ #Look for an executable called sphinx-build -find_program(SPHINX_EXECUTABLE - NAMES sphinx-build sphinx-build-3 - DOC "Path to sphinx-build executable") +find_program( + SPHINX_EXECUTABLE + NAMES sphinx-build sphinx-build-3 + DOC "Path to sphinx-build executable" +) include(FindPackageHandleStandardArgs) #Handle standard arguments to find_package like REQUIRED and QUIET -find_package_handle_standard_args(Sphinx - "Failed to find sphinx-build executable" - SPHINX_EXECUTABLE) +find_package_handle_standard_args( + Sphinx "Failed to find sphinx-build executable" SPHINX_EXECUTABLE +) diff --git a/cmake/FindnetCDF.cmake b/cmake/FindnetCDF.cmake index 393c57549b..ca6eff8d75 100644 --- a/cmake/FindnetCDF.cmake +++ b/cmake/FindnetCDF.cmake @@ -28,118 +28,129 @@ include(BOUT++functions) include(CMakePrintHelpers) -if (NOT netCDF_ROOT AND EXISTS "${BOUT_USE_NETCDF}") +if(NOT netCDF_ROOT AND EXISTS "${BOUT_USE_NETCDF}") set(netCDF_ROOT "${BOUT_USE_NETCDF}") endif() +enable_language(C) find_package(netCDF QUIET CONFIG) -if (netCDF_FOUND) +if(netCDF_FOUND) message(STATUS "netCDF CONFIG found") set(netCDF_FOUND TRUE) - if (NOT TARGET netCDF::netcdf) + if(NOT TARGET netCDF::netcdf) bout_add_library_alias(netCDF::netcdf netcdf) endif() - if (netCDF_DEBUG) - cmake_print_properties(TARGETS netcdf PROPERTIES LOCATION VERSION) - endif (netCDF_DEBUG) + if(netCDF_DEBUG) + cmake_print_properties(TARGETS netcdf PROPERTIES LOCATION VERSION) + endif(netCDF_DEBUG) return() endif() -find_program(NC_CONFIG "nc-config" +find_program( + NC_CONFIG "nc-config" PATHS "${netCDF_ROOT}" PATH_SUFFIXES bin DOC "Path to netCDF C config helper" NO_DEFAULT_PATH - ) +) -find_program(NC_CONFIG "nc-config" - DOC "Path to netCDF C config helper" - ) +find_program(NC_CONFIG "nc-config" DOC "Path to netCDF C config helper") get_filename_component(NC_CONFIG_TMP "${NC_CONFIG}" DIRECTORY) get_filename_component(NC_CONFIG_LOCATION "${NC_CONFIG_TMP}" DIRECTORY) -if (netCDF_DEBUG) +if(netCDF_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " NC_CONFIG_LOCATION = ${NC_CONFIG_LOCATION}" - " netCDF_ROOT = ${netCDF_ROOT}") + " NC_CONFIG_LOCATION = ${NC_CONFIG_LOCATION}" + " netCDF_ROOT = ${netCDF_ROOT}" + ) endif() bout_inspect_netcdf_config(NC_HINTS_INCLUDE_DIR "${NC_CONFIG}" "--includedir") bout_inspect_netcdf_config(NC_HINTS_PREFIX "${NC_CONFIG}" "--prefix") -find_path(netCDF_C_INCLUDE_DIR +find_path( + netCDF_C_INCLUDE_DIR NAMES netcdf.h DOC "netCDF C include directories" - HINTS - "${NC_HINTS_INCLUDE_DIR}" - "${NC_HINTS_PREFIX}" - "${NC_CONFIG_LOCATION}" - PATH_SUFFIXES - "include" + HINTS "${NC_HINTS_INCLUDE_DIR}" "${NC_HINTS_PREFIX}" "${NC_CONFIG_LOCATION}" + PATH_SUFFIXES "include" +) +if(netCDF_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " netCDF_C_INCLUDE_DIR = ${netCDF_C_INCLUDE_DIR}" + " NC_HINTS_INCLUDE_DIR = ${NC_HINTS_INCLUDE_DIR}" + " NC_HINTS_PREFIX = ${NC_HINTS_PREFIX}" ) -if (netCDF_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " netCDF_C_INCLUDE_DIR = ${netCDF_C_INCLUDE_DIR}" - " NC_HINTS_INCLUDE_DIR = ${NC_HINTS_INCLUDE_DIR}" - " NC_HINTS_PREFIX = ${NC_HINTS_PREFIX}" - ) endif() mark_as_advanced(netCDF_C_INCLUDE_DIR) -find_library(netCDF_C_LIBRARY +find_library( + netCDF_C_LIBRARY NAMES netcdf DOC "netCDF C library" - HINTS - "${NC_HINTS_INCLUDE_DIR}" - "${NC_HINTS_PREFIX}" - "${NC_CONFIG_LOCATION}" - PATH_SUFFIXES - "lib" "lib64" - ) -if (netCDF_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " netCDF_C_LIBRARY = ${netCDF_C_LIBRARY}" - " NC_HINTS_INCLUDE_DIR = ${NC_HINTS_INCLUDE_DIR}" - " NC_HINTS_PREFIX = ${NC_HINTS_PREFIX}" - ) + HINTS "${NC_HINTS_INCLUDE_DIR}" "${NC_HINTS_PREFIX}" "${NC_CONFIG_LOCATION}" + PATH_SUFFIXES "lib" "lib64" +) +if(netCDF_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " netCDF_C_LIBRARY = ${netCDF_C_LIBRARY}" + " NC_HINTS_INCLUDE_DIR = ${NC_HINTS_INCLUDE_DIR}" + " NC_HINTS_PREFIX = ${NC_HINTS_PREFIX}" + ) endif() mark_as_advanced(netCDF_C_LIBRARY) -if (netCDF_C_INCLUDE_DIR) +if(netCDF_C_INCLUDE_DIR) file(STRINGS "${netCDF_C_INCLUDE_DIR}/netcdf_meta.h" _netcdf_version_lines - REGEX "#define[ \t]+NC_VERSION_(MAJOR|MINOR|PATCH|NOTE)") - string(REGEX REPLACE ".*NC_VERSION_MAJOR *\([0-9]*\).*" "\\1" _netcdf_version_major "${_netcdf_version_lines}") - string(REGEX REPLACE ".*NC_VERSION_MINOR *\([0-9]*\).*" "\\1" _netcdf_version_minor "${_netcdf_version_lines}") - string(REGEX REPLACE ".*NC_VERSION_PATCH *\([0-9]*\).*" "\\1" _netcdf_version_patch "${_netcdf_version_lines}") - string(REGEX REPLACE ".*NC_VERSION_NOTE *\"\([^\"]*\)\".*" "\\1" _netcdf_version_note "${_netcdf_version_lines}") - if (NOT _netcdf_version_note STREQUAL "") + REGEX "#define[ \t]+NC_VERSION_(MAJOR|MINOR|PATCH|NOTE)" + ) + string(REGEX REPLACE ".*NC_VERSION_MAJOR *\([0-9]*\).*" "\\1" + _netcdf_version_major "${_netcdf_version_lines}" + ) + string(REGEX REPLACE ".*NC_VERSION_MINOR *\([0-9]*\).*" "\\1" + _netcdf_version_minor "${_netcdf_version_lines}" + ) + string(REGEX REPLACE ".*NC_VERSION_PATCH *\([0-9]*\).*" "\\1" + _netcdf_version_patch "${_netcdf_version_lines}" + ) + string(REGEX REPLACE ".*NC_VERSION_NOTE *\"\([^\"]*\)\".*" "\\1" + _netcdf_version_note "${_netcdf_version_lines}" + ) + if(NOT _netcdf_version_note STREQUAL "") # Make development version compare higher than any patch level set(_netcdf_version_note ".99") endif() - set(netCDF_VERSION "${_netcdf_version_major}.${_netcdf_version_minor}.${_netcdf_version_patch}${_netcdf_version_note}") + set(netCDF_VERSION + "${_netcdf_version_major}.${_netcdf_version_minor}.${_netcdf_version_patch}${_netcdf_version_note}" + ) unset(_netcdf_version_major) unset(_netcdf_version_minor) unset(_netcdf_version_patch) unset(_netcdf_version_note) unset(_netcdf_version_lines) -endif () +endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(netCDF +find_package_handle_standard_args( + netCDF REQUIRED_VARS netCDF_C_LIBRARY netCDF_C_INCLUDE_DIR - VERSION_VAR netCDF_VERSION) + VERSION_VAR netCDF_VERSION +) -if (netCDF_FOUND) +if(netCDF_FOUND) set(netCDF_INCLUDE_DIR "${netCDF_C_INCLUDE_DIR}") set(netCDF_INCLUDE_DIRS "${netCDF_C_INCLUDE_DIR}") set(netCDF_LIBRARIES "${netCDF_C_LIBRARY}") - if (NOT TARGET netCDF::netcdf) + if(NOT TARGET netCDF::netcdf) add_library(netCDF::netcdf UNKNOWN IMPORTED) - set_target_properties(netCDF::netcdf PROPERTIES - IMPORTED_LOCATION "${netCDF_C_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${netCDF_C_INCLUDE_DIR}" - ) - endif () -endif () + set_target_properties( + netCDF::netcdf + PROPERTIES IMPORTED_LOCATION "${netCDF_C_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${netCDF_C_INCLUDE_DIR}" + ) + endif() +endif() diff --git a/cmake/FindnetCDFCxx.cmake b/cmake/FindnetCDFCxx.cmake index d4155dc760..78bd93851e 100644 --- a/cmake/FindnetCDFCxx.cmake +++ b/cmake/FindnetCDFCxx.cmake @@ -27,16 +27,16 @@ include(BOUT++functions) -if (NOT netCDFCxx_ROOT AND EXISTS "${BOUT_USE_NETCDF}") +if(NOT netCDFCxx_ROOT AND EXISTS "${BOUT_USE_NETCDF}") set(netCDFCxx_ROOT "${BOUT_USE_NETCDF}") endif() -if (NOT EXISTS ${NCXX4_CONFIG}) +if(NOT EXISTS ${NCXX4_CONFIG}) # Only search if NCXX4_CONFIG was not set explicitly find_package(netCDFCxx QUIET CONFIG) - if (netCDFCxx_FOUND) + if(netCDFCxx_FOUND) set(netCDFCxx_FOUND TRUE) - if (NOT TARGET netCDF::netcdf-cxx4) + if(NOT TARGET netCDF::netcdf-cxx4) bout_add_library_alias(netCDF::netcdf-cxx4 netcdf-cxx4) endif() return() @@ -45,73 +45,74 @@ endif() find_package(netCDF REQUIRED) -find_program(NCXX4_CONFIG "ncxx4-config" +find_program( + NCXX4_CONFIG "ncxx4-config" PATHS "${netCDFCxx_ROOT}" PATH_SUFFIXES bin DOC "Path to netCDF C++ config helper" NO_DEFAULT_PATH - ) +) -find_program(NCXX4_CONFIG "ncxx4-config" - DOC "Path to netCDF C++ config helper" - ) +find_program(NCXX4_CONFIG "ncxx4-config" DOC "Path to netCDF C++ config helper") get_filename_component(NCXX4_CONFIG_TMP "${NCXX4_CONFIG}" DIRECTORY) get_filename_component(NCXX4_CONFIG_LOCATION "${NCXX4_CONFIG_TMP}" DIRECTORY) -if (netCDFCxx_DEBUG) +if(netCDFCxx_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " NCXX4_CONFIG_LOCATION = ${NCXX4_CONFIG_LOCATION}") + " NCXX4_CONFIG_LOCATION = ${NCXX4_CONFIG_LOCATION}" + ) endif() -bout_inspect_netcdf_config(NCXX4_HINTS_INCLUDE_DIR "${NCXX4_CONFIG}" "--includedir") +bout_inspect_netcdf_config( + NCXX4_HINTS_INCLUDE_DIR "${NCXX4_CONFIG}" "--includedir" +) bout_inspect_netcdf_config(NCXX4_HINTS_PREFIX "${NCXX4_CONFIG}" "--prefix") -find_path(netCDF_CXX_INCLUDE_DIR +find_path( + netCDF_CXX_INCLUDE_DIR NAMES netcdf DOC "netCDF C++ include directories" - HINTS - "${netCDF_C_INCLUDE_DIR}" - "${NCXX4_HINTS_INCLUDE_DIR}" - "${NCXX4_HINTS_PREFIX}" - "${NCXX4_CONFIG_LOCATION}" - PATH_SUFFIXES - "include" + HINTS "${netCDF_C_INCLUDE_DIR}" "${NCXX4_HINTS_INCLUDE_DIR}" + "${NCXX4_HINTS_PREFIX}" "${NCXX4_CONFIG_LOCATION}" + PATH_SUFFIXES "include" +) +if(netCDFCxx_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " netCDF_CXX_INCLUDE_DIR = ${netCDF_CXX_INCLUDE_DIR}" + " NCXX4_HINTS_INCLUDE_DIR = ${NCXX4_HINTS_INCLUDE_DIR}" + " NCXX4_HINTS_PREFIX = ${NCXX4_HINTS_PREFIX}" ) -if (netCDFCxx_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " netCDF_CXX_INCLUDE_DIR = ${netCDF_CXX_INCLUDE_DIR}" - " NCXX4_HINTS_INCLUDE_DIR = ${NCXX4_HINTS_INCLUDE_DIR}" - " NCXX4_HINTS_PREFIX = ${NCXX4_HINTS_PREFIX}" - ) endif() mark_as_advanced(netCDF_CXX_INCLUDE_DIR) -find_library(netCDF_CXX_LIBRARY +find_library( + netCDF_CXX_LIBRARY NAMES netcdf_c++4 netcdf-cxx4 DOC "netCDF C++ library" - HINTS - "${NCXX4_HINTS_INCLUDE_DIR}" - "${NCXX4_HINTS_PREFIX}" - "${NCXX4_CONFIG_LOCATION}" - PATH_SUFFIXES - "lib" "lib64" + HINTS "${NCXX4_HINTS_INCLUDE_DIR}" "${NCXX4_HINTS_PREFIX}" + "${NCXX4_CONFIG_LOCATION}" + PATH_SUFFIXES "lib" "lib64" +) +if(netCDFCxx_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " netCDF_CXX_LIBRARY = ${netCDF_CXX_LIBRARY}" + " NCXX4_HINTS_INCLUDE_DIR = ${NCXX4_HINTS_INCLUDE_DIR}" + " NCXX4_HINTS_PREFIX = ${NCXX4_HINTS_PREFIX}" ) -if (netCDFCxx_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " netCDF_CXX_LIBRARY = ${netCDF_CXX_LIBRARY}" - " NCXX4_HINTS_INCLUDE_DIR = ${NCXX4_HINTS_INCLUDE_DIR}" - " NCXX4_HINTS_PREFIX = ${NCXX4_HINTS_PREFIX}" - ) endif() mark_as_advanced(netCDF_CXX_LIBRARY) bout_inspect_netcdf_config(_ncxx4_version "${NCXX4_CONFIG}" "--version") -if (_ncxx4_version) +if(_ncxx4_version) # Change to lower case before matching, to avoid case problems string(TOLOWER "${_ncxx4_version}" _ncxx4_version_lower) - string(REGEX REPLACE "netcdf-cxx4 \([0-9]+\\.[0-9]+\\.[0-9]+\).*" "\\1" netCDFCxx_VERSION "${_ncxx4_version_lower}") + string(REGEX REPLACE "netcdf-cxx4 \([0-9]+\\.[0-9]+\\.[0-9]+\).*" "\\1" + netCDFCxx_VERSION "${_ncxx4_version_lower}" + ) message(STATUS "Found netCDFCxx version ${netCDFCxx_VERSION}") -else () +else() message(WARNING "Couldn't get NetCDF version") endif() @@ -121,19 +122,23 @@ unset(_netcdf_version_minor) unset(_netcdf_version_patch) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(netCDFCxx +find_package_handle_standard_args( + netCDFCxx REQUIRED_VARS netCDF_CXX_LIBRARY netCDF_CXX_INCLUDE_DIR - VERSION_VAR netCDFCxx_VERSION) + VERSION_VAR netCDFCxx_VERSION +) -if (netCDFCxx_FOUND) +if(netCDFCxx_FOUND) set(netCDFCxx_INCLUDE_DIRS "${netCDF_CXX_INCLUDE_DIR}") set(netCDFCxx_LIBRARIES "${netCDF_CXX_LIBRARY}") - if (NOT TARGET netCDF::netcdf-cxx4) + if(NOT TARGET netCDF::netcdf-cxx4) add_library(netCDF::netcdf-cxx4 UNKNOWN IMPORTED) - set_target_properties(netCDF::netcdf-cxx4 PROPERTIES - IMPORTED_LINK_INTERFACE_LIBRARIES netCDF::netcdf - IMPORTED_LOCATION "${netCDF_CXX_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${netCDF_CXX_INCLUDE_DIR}") - endif () -endif () + set_target_properties( + netCDF::netcdf-cxx4 + PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES netCDF::netcdf + IMPORTED_LOCATION "${netCDF_CXX_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${netCDF_CXX_INCLUDE_DIR}" + ) + endif() +endif() diff --git a/cmake/GenerateDateTimeFile.cmake b/cmake/GenerateDateTimeFile.cmake index ef48fc4638..8ed742b31a 100644 --- a/cmake/GenerateDateTimeFile.cmake +++ b/cmake/GenerateDateTimeFile.cmake @@ -2,6 +2,9 @@ # compilation date and time as variables set(bout_date_time_file - "const char* boutcompiledate{__DATE__}; const char* boutcompiletime{__TIME__};") + "const char* boutcompiledate{__DATE__}; const char* boutcompiletime{__TIME__};" +) -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx" "${bout_date_time_file}") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx" + "${bout_date_time_file}" +) diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake index 8ab03bc5f0..33f95dd589 100644 --- a/cmake/GetGitRevisionDescription.cmake +++ b/cmake/GetGitRevisionDescription.cmake @@ -37,7 +37,7 @@ # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) - return() + return() endif() set(__get_git_revision_description YES) @@ -46,123 +46,152 @@ set(__get_git_revision_description YES) get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) function(get_git_head_revision _refspecvar _hashvar) - set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories - set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") - get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) - if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) - # We have reached the root directory, we are not in git - set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - return() - endif() - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - endwhile() - # check if this is a submodule - if(NOT IS_DIRECTORY ${GIT_DIR}) - file(READ ${GIT_DIR} submodule) - string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) - get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) - get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) - endif() - set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") - if(NOT EXISTS "${GIT_DATA}") - file(MAKE_DIRECTORY "${GIT_DATA}") - endif() + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE + ) + set(${_hashvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component( + GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE + ) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() - if(NOT EXISTS "${GIT_DIR}/HEAD") - return() - endif() - set(HEAD_FILE "${GIT_DATA}/HEAD") - configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) - configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" - "${GIT_DATA}/grabRef.cmake" - @ONLY) - include("${GIT_DATA}/grabRef.cmake") + configure_file( + "${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY + ) + include("${GIT_DATA}/grabRef.cmake") - set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) - set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) + set(${_refspecvar} + "${HEAD_REF}" + PARENT_SCOPE + ) + set(${_hashvar} + "${HEAD_HASH}" + PARENT_SCOPE + ) endfunction() function(git_describe _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) - return() - endif() + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() - # TODO sanitize - #if((${ARGN}" MATCHES "&&") OR - # (ARGN MATCHES "||") OR - # (ARGN MATCHES "\\;")) - # message("Please report the following error to the project!") - # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") - #endif() + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() - #message(STATUS "Arguments to execute_process: ${ARGN}") + #message(STATUS "Arguments to execute_process: ${ARGN}") - execute_process(COMMAND - "${GIT_EXECUTABLE}" - describe - ${hash} - ${ARGN} - WORKING_DIRECTORY - "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() - set(${_var} "${out}" PARENT_SCOPE) + set(${_var} + "${out}" + PARENT_SCOPE + ) endfunction() function(git_get_exact_tag _var) - git_describe(out --exact-match ${ARGN}) - set(${_var} "${out}" PARENT_SCOPE) + git_describe(out --exact-match ${ARGN}) + set(${_var} + "${out}" + PARENT_SCOPE + ) endfunction() function(git_local_changes _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) - return() - endif() + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() - execute_process(COMMAND - "${GIT_EXECUTABLE}" - diff-index --quiet HEAD -- - WORKING_DIRECTORY - "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(res EQUAL 0) - set(${_var} "CLEAN" PARENT_SCOPE) - else() - set(${_var} "DIRTY" PARENT_SCOPE) - endif() + execute_process( + COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(res EQUAL 0) + set(${_var} + "CLEAN" + PARENT_SCOPE + ) + else() + set(${_var} + "DIRTY" + PARENT_SCOPE + ) + endif() endfunction() diff --git a/cmake/ResolveCompilerPaths.cmake b/cmake/ResolveCompilerPaths.cmake index 54787fa38f..d4274496df 100644 --- a/cmake/ResolveCompilerPaths.cmake +++ b/cmake/ResolveCompilerPaths.cmake @@ -38,68 +38,77 @@ # # assuming both directories exist. # Note: as currently implemented, the -I/string will be picked up mistakenly (cry, cry) -include (CorrectWindowsPaths) +include(CorrectWindowsPaths) -macro (RESOLVE_LIBRARIES LIBS LINK_LINE) - string (REGEX MATCHALL "((-L|-l|-Wl)([^\" ]+|\"[^\"]+\")|[^\" ]+\\.(a|so|dll|lib))" _all_tokens "${LINK_LINE}") - set (_libs_found "") - set (_directory_list "") - foreach (token ${_all_tokens}) - if (token MATCHES "-L([^\" ]+|\"[^\"]+\")") +macro(RESOLVE_LIBRARIES LIBS LINK_LINE) + string(REGEX MATCHALL + "((-L|-l|-Wl)([^\" ]+|\"[^\"]+\")|[^\" ]+\\.(a|so|dll|lib))" + _all_tokens "${LINK_LINE}" + ) + set(_libs_found "") + set(_directory_list "") + foreach(token ${_all_tokens}) + if(token MATCHES "-L([^\" ]+|\"[^\"]+\")") # If it's a library path, add it to the list - string (REGEX REPLACE "^-L" "" token ${token}) - string (REGEX REPLACE "//" "/" token ${token}) + string(REGEX REPLACE "^-L" "" token ${token}) + string(REGEX REPLACE "//" "/" token ${token}) convert_cygwin_path(token) - list (APPEND _directory_list ${token}) - elseif (token MATCHES "^(-l([^\" ]+|\"[^\"]+\")|[^\" ]+\\.(a|so|dll|lib))") + list(APPEND _directory_list ${token}) + elseif(token MATCHES "^(-l([^\" ]+|\"[^\"]+\")|[^\" ]+\\.(a|so|dll|lib))") # It's a library, resolve the path by looking in the list and then (by default) in system directories - if (WIN32) #windows expects "libfoo", linux expects "foo" - string (REGEX REPLACE "^-l" "lib" token ${token}) - else (WIN32) - string (REGEX REPLACE "^-l" "" token ${token}) - endif (WIN32) - set (_root "") - if (token MATCHES "^/") # We have an absolute path + if(WIN32) #windows expects "libfoo", linux expects "foo" + string(REGEX REPLACE "^-l" "lib" token ${token}) + else(WIN32) + string(REGEX REPLACE "^-l" "" token ${token}) + endif(WIN32) + set(_root "") + if(token MATCHES "^/") # We have an absolute path #separate into a path and a library name: - string (REGEX MATCH "[^/]*\\.(a|so|dll|lib)$" libname ${token}) - string (REGEX MATCH ".*[^${libname}$]" libpath ${token}) + string(REGEX MATCH "[^/]*\\.(a|so|dll|lib)$" libname ${token}) + string(REGEX MATCH ".*[^${libname}$]" libpath ${token}) convert_cygwin_path(libpath) - set (_directory_list ${_directory_list} ${libpath}) - set (token ${libname}) - endif (token MATCHES "^/") - set (_lib "NOTFOUND" CACHE FILEPATH "Cleared" FORCE) - find_library (_lib ${token} HINTS ${_directory_list} ${_root}) - if (_lib) - string (REPLACE "//" "/" _lib ${_lib}) - list (APPEND _libs_found ${_lib}) - else (_lib) - message (STATUS "Unable to find library ${token}") - endif (_lib) - endif (token MATCHES "-L([^\" ]+|\"[^\"]+\")") - endforeach (token) - set (_lib "NOTFOUND" CACHE INTERNAL "Scratch variable" FORCE) + set(_directory_list ${_directory_list} ${libpath}) + set(token ${libname}) + endif(token MATCHES "^/") + set(_lib + "NOTFOUND" + CACHE FILEPATH "Cleared" FORCE + ) + find_library(_lib ${token} HINTS ${_directory_list} ${_root}) + if(_lib) + string(REPLACE "//" "/" _lib ${_lib}) + list(APPEND _libs_found ${_lib}) + else(_lib) + message(STATUS "Unable to find library ${token}") + endif(_lib) + endif(token MATCHES "-L([^\" ]+|\"[^\"]+\")") + endforeach(token) + set(_lib + "NOTFOUND" + CACHE INTERNAL "Scratch variable" FORCE + ) # only the LAST occurence of each library is required since there should be no circular dependencies - if (_libs_found) - list (REVERSE _libs_found) - list (REMOVE_DUPLICATES _libs_found) - list (REVERSE _libs_found) - endif (_libs_found) - set (${LIBS} "${_libs_found}") -endmacro (RESOLVE_LIBRARIES) + if(_libs_found) + list(REVERSE _libs_found) + list(REMOVE_DUPLICATES _libs_found) + list(REVERSE _libs_found) + endif(_libs_found) + set(${LIBS} "${_libs_found}") +endmacro(RESOLVE_LIBRARIES) -macro (RESOLVE_INCLUDES INCS COMPILE_LINE) - string (REGEX MATCHALL "-I([^\" ]+|\"[^\"]+\")" _all_tokens "${COMPILE_LINE}") - set (_incs_found "") - foreach (token ${_all_tokens}) - string (REGEX REPLACE "^-I" "" token ${token}) - string (REGEX REPLACE "//" "/" token ${token}) +macro(RESOLVE_INCLUDES INCS COMPILE_LINE) + string(REGEX MATCHALL "-I([^\" ]+|\"[^\"]+\")" _all_tokens "${COMPILE_LINE}") + set(_incs_found "") + foreach(token ${_all_tokens}) + string(REGEX REPLACE "^-I" "" token ${token}) + string(REGEX REPLACE "//" "/" token ${token}) convert_cygwin_path(token) - if (EXISTS ${token}) - list (APPEND _incs_found ${token}) - else (EXISTS ${token}) - message (STATUS "Include directory ${token} does not exist") - endif (EXISTS ${token}) - endforeach (token) - list (REMOVE_DUPLICATES _incs_found) - set (${INCS} "${_incs_found}") -endmacro (RESOLVE_INCLUDES) + if(EXISTS ${token}) + list(APPEND _incs_found ${token}) + else(EXISTS ${token}) + message(STATUS "Include directory ${token} does not exist") + endif(EXISTS ${token}) + endforeach(token) + list(REMOVE_DUPLICATES _incs_found) + set(${INCS} "${_incs_found}") +endmacro(RESOLVE_INCLUDES) diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake index 715a08ab88..aab58d6f70 100644 --- a/cmake/Sanitizers.cmake +++ b/cmake/Sanitizers.cmake @@ -4,7 +4,9 @@ function(enable_sanitizers target_name) - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + ".*Clang" + ) option(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE) message(STATUS "Enable coverage: ${ENABLE_COVERAGE}") @@ -17,33 +19,40 @@ function(enable_sanitizers target_name) find_program(genhtml_FOUND genhtml) message(STATUS "Looking for genhtml: ${genhtml_FOUND}") - if (lcov_FOUND AND genhtml_FOUND) - set(COVERAGE_NAME coverage CACHE STRING "Name of coverage output file") + if(lcov_FOUND AND genhtml_FOUND) + set(COVERAGE_NAME + coverage + CACHE STRING "Name of coverage output file" + ) set(COVERAGE_FILE "${COVERAGE_NAME}.info") - set(COVERAGE_MSG "Open file://${PROJECT_SOURCE_DIR}/${COVERAGE_NAME}/index.html in your browser to view coverage HTML output") + set(COVERAGE_MSG + "Open file://${PROJECT_SOURCE_DIR}/${COVERAGE_NAME}/index.html in your browser to view coverage HTML output" + ) - add_custom_target(code-coverage-capture + add_custom_target( + code-coverage-capture COMMAND - lcov -c --directory "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/bout++.dir/src" - --output-file "${COVERAGE_FILE}" - COMMAND - genhtml --output-directory "${COVERAGE_NAME}" --demangle-cpp --legend --show-details "${COVERAGE_FILE}" - COMMAND - "${CMAKE_COMMAND}" -E echo ${COVERAGE_MSG} + lcov -c --directory + "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/bout++.dir/src" + --output-file "${COVERAGE_FILE}" + COMMAND genhtml --output-directory "${COVERAGE_NAME}" --demangle-cpp + --legend --show-details "${COVERAGE_FILE}" + COMMAND "${CMAKE_COMMAND}" -E echo ${COVERAGE_MSG} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Capturing coverage information" - BYPRODUCTS - "${COVERAGE_FILE}" - "${COVERAGE_NAME}/index.html" - ) + BYPRODUCTS "${COVERAGE_FILE}" "${COVERAGE_NAME}/index.html" + ) - add_custom_target(code-coverage-clean - COMMAND - lcov --zerocounters + add_custom_target( + code-coverage-clean + COMMAND lcov --zerocounters COMMENT "Cleaning coverage information" - ) + ) else() - message(FATAL_ERROR "Coverage enabled, but coverage-capture not available. Please install lcov") + message( + FATAL_ERROR + "Coverage enabled, but coverage-capture not available. Please install lcov" + ) endif() endif() @@ -60,7 +69,9 @@ function(enable_sanitizers target_name) list(APPEND SANITIZERS "leak") endif() - option(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer" FALSE) + option(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR + "Enable undefined behavior sanitizer" FALSE + ) if(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR) list(APPEND SANITIZERS "undefined") endif() @@ -68,7 +79,10 @@ function(enable_sanitizers target_name) option(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" FALSE) if(ENABLE_SANITIZER_THREAD) if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) - message(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled") + message( + WARNING + "Thread sanitizer does not work with Address and Leak sanitizer enabled" + ) else() list(APPEND SANITIZERS "thread") endif() @@ -78,32 +92,40 @@ function(enable_sanitizers target_name) if(ENABLE_SANITIZER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") if("address" IN_LIST SANITIZERS OR "thread" IN_LIST SANITIZERS - OR "leak" IN_LIST SANITIZERS) - message(WARNING "Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled") + OR "leak" IN_LIST SANITIZERS + ) + message( + WARNING + "Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled" + ) else() list(APPEND SANITIZERS "memory") endif() endif() - list( - JOIN - SANITIZERS - "," - LIST_OF_SANITIZERS) + list(JOIN SANITIZERS "," LIST_OF_SANITIZERS) endif() # Default value gets overridden below - set(BOUT_USE_SANITIZERS "None" PARENT_SCOPE) + set(BOUT_USE_SANITIZERS + "None" + PARENT_SCOPE + ) if(LIST_OF_SANITIZERS) - if(NOT - "${LIST_OF_SANITIZERS}" - STREQUAL - "") - set(BOUT_USE_SANITIZERS ${LIST_OF_SANITIZERS} PARENT_SCOPE) - target_compile_options(${target_name} PUBLIC -fsanitize=${LIST_OF_SANITIZERS} -fno-omit-frame-pointer) - target_link_options(${target_name} PUBLIC -fsanitize=${LIST_OF_SANITIZERS}) + if(NOT "${LIST_OF_SANITIZERS}" STREQUAL "") + set(BOUT_USE_SANITIZERS + ${LIST_OF_SANITIZERS} + PARENT_SCOPE + ) + target_compile_options( + ${target_name} PUBLIC -fsanitize=${LIST_OF_SANITIZERS} + -fno-omit-frame-pointer + ) + target_link_options( + ${target_name} PUBLIC -fsanitize=${LIST_OF_SANITIZERS} + ) endif() endif() diff --git a/cmake/SetupBOUTThirdParty.cmake b/cmake/SetupBOUTThirdParty.cmake index 10942f8aa9..f0c98548ea 100644 --- a/cmake/SetupBOUTThirdParty.cmake +++ b/cmake/SetupBOUTThirdParty.cmake @@ -1,12 +1,12 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") # determined in SetupCompilers.cmake -if (BOUT_USE_MPI) +if(BOUT_USE_MPI) target_link_libraries(bout++ PUBLIC MPI::MPI_CXX) -endif () +endif() # determined in SetupCompilers.cmake -if (BOUT_USE_OPENMP) +if(BOUT_USE_OPENMP) target_link_libraries(bout++ PUBLIC OpenMP::OpenMP_CXX) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -fopenmp") set(CONFIG_LDFLAGS_SHARED "${CONFIG_LDFLAGS_SHARED} -fopenmp") @@ -14,7 +14,7 @@ if (BOUT_USE_OPENMP) endif() # determined in SetupCompilers.cmake -if (BOUT_HAS_CUDA) +if(BOUT_HAS_CUDA) enable_language(CUDA) message(STATUS "BOUT_HAS_CUDA ${CMAKE_CUDA_COMPILER}") @@ -29,58 +29,65 @@ if (BOUT_HAS_CUDA) set_target_properties(bout++ PROPERTIES CUDA_SEPARABLE_COMPILATION ON) set_target_properties(bout++ PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(bout++ PROPERTIES LINKER_LANGUAGE CUDA) -endif () +endif() # Caliper option(BOUT_ENABLE_CALIPER "Enable Caliper" OFF) -if (BOUT_ENABLE_CALIPER) +if(BOUT_ENABLE_CALIPER) find_package(caliper REQUIRED) target_include_directories(bout++ PUBLIC ${caliper_INCLUDE_DIR}) target_link_libraries(bout++ PUBLIC caliper) -endif () +endif() set(BOUT_HAS_CALIPER ${BOUT_ENABLE_CALIPER}) # UMPIRE option(BOUT_ENABLE_UMPIRE "Enable UMPIRE memory management" OFF) -if (BOUT_ENABLE_UMPIRE) +if(BOUT_ENABLE_UMPIRE) find_package(UMPIRE REQUIRED) target_include_directories(bout++ PUBLIC ${UMPIRE_INCLUDE_DIRS}/include) target_link_libraries(bout++ PUBLIC umpire) -endif () +endif() set(BOUT_HAS_UMPIRE ${BOUT_ENABLE_UMPIRE}) # RAJA option(BOUT_ENABLE_RAJA "Enable RAJA" OFF) -if (BOUT_ENABLE_RAJA) +if(BOUT_ENABLE_RAJA) find_package(RAJA REQUIRED) - message(STATUS "RAJA_CONFIG:" ${RAJA_CONFIG}) + message(STATUS "RAJA_CONFIG:" ${RAJA_CONFIG}) string(FIND ${RAJA_CONFIG} "raja" loc) math(EXPR value "${loc} + 5" OUTPUT_FORMAT DECIMAL) - string(SUBSTRING ${RAJA_CONFIG} 0 ${value} RAJA_PATH) + string(SUBSTRING ${RAJA_CONFIG} 0 ${value} RAJA_PATH) message(STATUS "RAJA_PATH" ${RAJA_PATH}) target_include_directories(bout++ PUBLIC ${RAJA_PATH}/include) target_link_libraries(bout++ PUBLIC RAJA) -endif () +endif() set(BOUT_HAS_RAJA ${BOUT_ENABLE_RAJA}) # Hypre option(BOUT_USE_HYPRE "Enable support for Hypre solvers" OFF) -if (BOUT_USE_HYPRE) +if(BOUT_USE_HYPRE) enable_language(C) find_package(HYPRE REQUIRED) target_link_libraries(bout++ PUBLIC HYPRE::HYPRE) - if (HYPRE_WITH_CUDA AND BOUT_HAS_CUDA) - target_compile_definitions(bout++ PUBLIC "HYPRE_USING_CUDA;HYPRE_USING_UNIFIED_MEMORY") - target_link_libraries(bout++ PUBLIC CUDA::cusparse CUDA::curand CUDA::culibos CUDA::cublas CUDA::cublasLt) - endif () -endif () + if(HYPRE_WITH_CUDA AND BOUT_HAS_CUDA) + target_compile_definitions( + bout++ PUBLIC "HYPRE_USING_CUDA;HYPRE_USING_UNIFIED_MEMORY" + ) + target_link_libraries( + bout++ PUBLIC CUDA::cusparse CUDA::curand CUDA::culibos CUDA::cublas + CUDA::cublasLt + ) + endif() +endif() message(STATUS "HYPRE support: ${BOUT_USE_HYPRE}") set(BOUT_HAS_HYPRE ${BOUT_USE_HYPRE}) # PETSc -option(BOUT_USE_PETSC "Enable support for PETSc time solvers and inversions" OFF) -if (BOUT_USE_PETSC) - if (NOT CMAKE_SYSTEM_NAME STREQUAL "CrayLinuxEnvironment") +option(BOUT_USE_PETSC "Enable support for PETSc time solvers and inversions" + OFF +) +if(BOUT_USE_PETSC) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "CrayLinuxEnvironment") # Cray wrappers sort this out for us find_package(PETSc REQUIRED) target_link_libraries(bout++ PUBLIC PETSc::PETSc) @@ -94,28 +101,44 @@ endif() message(STATUS "PETSc support: ${BOUT_USE_PETSC}") set(BOUT_HAS_PETSC ${BOUT_USE_PETSC}) - -cmake_dependent_option(BOUT_USE_SYSTEM_MPARK_VARIANT "Use external installation of mpark.variant" OFF - "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/mpark.variant/CMakeLists.txt" ON) +cmake_dependent_option( + BOUT_USE_SYSTEM_MPARK_VARIANT + "Use external installation of mpark.variant" + OFF + "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/mpark.variant/CMakeLists.txt" + ON +) if(BOUT_USE_SYSTEM_MPARK_VARIANT) message(STATUS "Using external mpark.variant") find_package(mpark_variant REQUIRED) - get_target_property(MPARK_VARIANT_INCLUDE_PATH mpark_variant INTERFACE_INCLUDE_DIRECTORIES) + get_target_property( + MPARK_VARIANT_INCLUDE_PATH mpark_variant INTERFACE_INCLUDE_DIRECTORIES + ) else() message(STATUS "Using mpark.variant submodule") bout_update_submodules() add_subdirectory(externalpackages/mpark.variant) if(NOT TARGET mpark_variant) - message(FATAL_ERROR "mpark_variant not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?") + message( + FATAL_ERROR + "mpark_variant not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?" + ) endif() - set(MPARK_VARIANT_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/mpark.variant/include") + set(MPARK_VARIANT_INCLUDE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/mpark.variant/include" + ) set(CONFIG_CFLAGS "${CONFIG_CFLAGS} -I\${MPARK_VARIANT_INCLUDE_PATH}") endif() target_link_libraries(bout++ PUBLIC mpark_variant) -cmake_dependent_option(BOUT_USE_SYSTEM_FMT "Use external installation of fmt" OFF - "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/fmt/CMakeLists.txt" ON) +cmake_dependent_option( + BOUT_USE_SYSTEM_FMT + "Use external installation of fmt" + OFF + "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/fmt/CMakeLists.txt" + ON +) if(BOUT_USE_SYSTEM_FMT) message(STATUS "Using external fmt") @@ -125,32 +148,46 @@ else() message(STATUS "Using fmt submodule") bout_update_submodules() # Need to install fmt alongside BOUT++ - set(FMT_INSTALL ON CACHE BOOL "") - set(FMT_DEBUG_POSTFIX "" CACHE STRING "") + set(FMT_INSTALL + ON + CACHE BOOL "" + ) + set(FMT_DEBUG_POSTFIX + "" + CACHE STRING "" + ) add_subdirectory(externalpackages/fmt) if(NOT TARGET fmt::fmt) - message(FATAL_ERROR "fmt not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?") + message( + FATAL_ERROR + "fmt not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?" + ) endif() # Build the library in /lib: this makes updating the path # for bout-config much easier - set_target_properties(fmt PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" - ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") - set(FMT_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/fmt/include") + set_target_properties( + fmt PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" + ) + set(FMT_INCLUDE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/fmt/include" + ) set(CONFIG_CFLAGS "${CONFIG_CFLAGS} -I\${FMT_INCLUDE_PATH}") set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -lfmt") endif() target_link_libraries(bout++ PUBLIC fmt::fmt) option(BOUT_USE_PVODE "Enable support for bundled PVODE" ON) -if (BOUT_USE_PVODE) +if(BOUT_USE_PVODE) add_subdirectory(externalpackages/PVODE) target_link_libraries(bout++ PUBLIC pvode pvpre) # Build the libraries in /lib: this makes updating the # path for bout-config much easier - set_target_properties(pvode pvpre PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" - ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") + set_target_properties( + pvode pvpre + PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" + ) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -lpvode -lpvpre") endif() message(STATUS "PVODE support: ${BOUT_USE_PVODE}") @@ -158,25 +195,30 @@ set(BOUT_HAS_PVODE ${BOUT_USE_PVODE}) option(BOUT_USE_NETCDF "Enable support for NetCDF output" ON) option(BOUT_DOWNLOAD_NETCDF_CXX4 "Download and build netCDF-cxx4" OFF) -if (BOUT_USE_NETCDF) - if (BOUT_DOWNLOAD_NETCDF_CXX4) +if(BOUT_USE_NETCDF) + if(BOUT_DOWNLOAD_NETCDF_CXX4) message(STATUS "Downloading and configuring NetCDF-cxx4") include(FetchContent) FetchContent_Declare( netcdf-cxx4 - GIT_REPOSITORY https://github.com/ZedThree/netcdf-cxx4 - GIT_TAG "ad3e50953190615cb69dcc8a4652f9a88a8499cf" - ) + GIT_REPOSITORY https://github.com/Unidata/netcdf-cxx4 + GIT_TAG "a43d6d4d415d407712c246faca553bd951730dc1" + ) # Don't build the netcdf tests, they have lots of warnings - set(NCXX_ENABLE_TESTS OFF CACHE BOOL "" FORCE) + set(NCXX_ENABLE_TESTS + OFF + CACHE BOOL "" FORCE + ) # Use our own FindnetCDF module which uses nc-config find_package(netCDF REQUIRED) FetchContent_MakeAvailable(netcdf-cxx4) target_link_libraries(bout++ PUBLIC netCDF::netcdf-cxx4) else() find_package(netCDFCxx) - if (netCDFCxx_FOUND) - set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${netCDF_CXX_LIBRARY} ${netCDF_LIBRARIES}") + if(netCDFCxx_FOUND) + set(CONFIG_LDFLAGS + "${CONFIG_LDFLAGS} ${netCDF_CXX_LIBRARY} ${netCDF_LIBRARIES}" + ) target_link_libraries(bout++ PUBLIC netCDF::netcdf-cxx4) else() find_package(PkgConfig REQUIRED) @@ -186,17 +228,23 @@ if (BOUT_USE_NETCDF) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${NETCDF_LDFLAGS_STRING}") endif() endif() + if(netCDF_DIR) + set(netCDF_ROOT "${netCDF_DIR}") + endif() + if(netCDFCxx_DIR) + set(netCDFCxx_ROOT "${netCDFCxx_DIR}") + endif() endif() message(STATUS "NetCDF support: ${BOUT_USE_NETCDF}") set(BOUT_HAS_NETCDF ${BOUT_USE_NETCDF}) option(BOUT_USE_ADIOS2 "Enable support for ADIOS output" OFF) option(BOUT_DOWNLOAD_ADIOS2 "Download and build ADIOS2" OFF) -if (BOUT_USE_ADIOS2) +if(BOUT_USE_ADIOS2) enable_language(C) find_package(MPI REQUIRED COMPONENTS C) - if (BOUT_DOWNLOAD_ADIOS2) + if(BOUT_DOWNLOAD_ADIOS2) message(STATUS "Downloading and configuring ADIOS2") include(FetchContent) FetchContent_Declare( @@ -204,28 +252,60 @@ if (BOUT_USE_ADIOS2) GIT_REPOSITORY https://github.com/ornladios/ADIOS2.git GIT_TAG origin/master GIT_SHALLOW 1 - ) - set(ADIOS2_USE_MPI ON CACHE BOOL "" FORCE) - set(ADIOS2_USE_Fortran OFF CACHE BOOL "" FORCE) - set(ADIOS2_USE_Python OFF CACHE BOOL "" FORCE) - set(ADIOS2_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + ) + set(ADIOS2_USE_MPI + ON + CACHE BOOL "" FORCE + ) + set(ADIOS2_USE_Fortran + OFF + CACHE BOOL "" FORCE + ) + set(ADIOS2_USE_Python + OFF + CACHE BOOL "" FORCE + ) + set(ADIOS2_BUILD_EXAMPLES + OFF + CACHE BOOL "" FORCE + ) # Disable testing, or ADIOS will try to find or install GTEST - set(BUILD_TESTING OFF CACHE BOOL "" FORCE) + set(BUILD_TESTING + OFF + CACHE BOOL "" FORCE + ) # Note: SST requires but doesn't check at configure time - set(ADIOS2_USE_SST OFF CACHE BOOL "" FORCE) + set(ADIOS2_USE_SST + OFF + CACHE BOOL "" FORCE + ) FetchContent_MakeAvailable(adios2) message(STATUS "ADIOS2 done configuring") else() find_package(ADIOS2 REQUIRED) endif() - target_link_libraries(bout++ PUBLIC adios2::cxx11_mpi MPI::MPI_C) + + foreach(_adios2_candidate adios2::cxx_mpi adios2::cxx20_mpi adios2::cxx17_mpi + adios2::cxx11_mpi + ) + if(TARGET ${_adios2_candidate}) + set(_adios2_target ${_adios2_candidate}) + break() + endif() + endforeach() + + if(NOT DEFINED _adios2_target) + message(FATAL_ERROR "Could not find a usable ADIOS2 CXX CMake target. ") + endif() + message(STATUS "Using ADIOS2 CMake target: ${_adios2_target}") + + target_link_libraries(bout++ PUBLIC ${_adios2_target} MPI::MPI_C) endif() message(STATUS "ADIOS2 support: ${BOUT_USE_ADIOS2}") set(BOUT_HAS_ADIOS2 ${BOUT_USE_ADIOS2}) - option(BOUT_USE_FFTW "Enable support for FFTW" ON) -if (BOUT_USE_FFTW) +if(BOUT_USE_FFTW) find_package(FFTW REQUIRED) target_link_libraries(bout++ PUBLIC FFTW::FFTW) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${FFTW_LIBRARIES}") @@ -238,15 +318,15 @@ option(BOUT_USE_LAPACK "Enable support for LAPACK" AUTO) set_property(CACHE BOUT_USE_LAPACK PROPERTY STRINGS ${ON_OFF_AUTO}) set(LAPACK_FOUND OFF) -if (BOUT_USE_LAPACK) - if (NOT CMAKE_SYSTEM_NAME STREQUAL "CrayLinuxEnvironment") +if(BOUT_USE_LAPACK) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "CrayLinuxEnvironment") # Cray wrappers sort this out for us - if (BOUT_USE_LAPACK STREQUAL ON) + if(BOUT_USE_LAPACK STREQUAL ON) find_package(LAPACK REQUIRED) else() find_package(LAPACK) endif() - if (LAPACK_FOUND) + if(LAPACK_FOUND) target_link_libraries(bout++ PUBLIC "${LAPACK_LIBRARIES}") string(JOIN " " CONFIG_LAPACK_LIBRARIES ${LAPACK_LIBRARIES}) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${CONFIG_LAPACK_LIBRARIES}") @@ -258,7 +338,7 @@ message(STATUS "LAPACK support: ${LAPACK_FOUND}") set(BOUT_HAS_LAPACK ${LAPACK_FOUND}) option(BOUT_USE_SLEPC "Enable support for SLEPc eigen solver" OFF) -if (BOUT_USE_SLEPC) +if(BOUT_USE_SLEPC) find_package(SLEPc REQUIRED) target_link_libraries(bout++ PUBLIC SLEPc::SLEPc) string(JOIN " " CONFIG_SLEPC_LIBRARIES ${SLEPC_LIBRARIES}) @@ -269,41 +349,78 @@ set(BOUT_HAS_SLEPC ${BOUT_USE_SLEPC}) option(BOUT_DOWNLOAD_SUNDIALS "Download and build SUNDIALS" OFF) # Force BOUT_USE_SUNDIALS if we're downloading it! -cmake_dependent_option(BOUT_USE_SUNDIALS "Enable support for SUNDIALS time solvers" OFF - "NOT BOUT_DOWNLOAD_SUNDIALS" ON) -if (BOUT_USE_SUNDIALS) - if (BOUT_DOWNLOAD_SUNDIALS) +cmake_dependent_option( + BOUT_USE_SUNDIALS "Enable support for SUNDIALS time solvers" OFF + "NOT BOUT_DOWNLOAD_SUNDIALS" ON +) +set(BOUT_HAS_SUNDIALS_MANYVECTOR OFF) +if(BOUT_USE_SUNDIALS) + enable_language(C) + if(BOUT_DOWNLOAD_SUNDIALS) message(STATUS "Downloading and configuring SUNDIALS") include(FetchContent) FetchContent_Declare( sundials GIT_REPOSITORY https://github.com/LLNL/sundials - GIT_TAG v7.2.1 - ) + GIT_TAG v7.2.1 + ) # Note: These are settings for building SUNDIALS - set(EXAMPLES_ENABLE_C OFF CACHE BOOL "" FORCE) - set(EXAMPLES_INSTALL OFF CACHE BOOL "" FORCE) - set(ENABLE_MPI ${BOUT_USE_MPI} CACHE BOOL "" FORCE) - set(ENABLE_OPENMP OFF CACHE BOOL "" FORCE) - if (BUILD_SHARED_LIBS) - set(BUILD_STATIC_LIBS OFF CACHE BOOL "" FORCE) + set(EXAMPLES_ENABLE_C + OFF + CACHE BOOL "" FORCE + ) + set(EXAMPLES_INSTALL + OFF + CACHE BOOL "" FORCE + ) + set(ENABLE_MPI + ${BOUT_USE_MPI} + CACHE BOOL "" FORCE + ) + set(ENABLE_OPENMP + ${BOUT_USE_OPENMP} + CACHE BOOL "" FORCE + ) + if(BUILD_SHARED_LIBS) + set(BUILD_STATIC_LIBS + OFF + CACHE BOOL "" FORCE + ) else() - set(BUILD_STATIC_LIBS ON CACHE BOOL "" FORCE) + set(BUILD_STATIC_LIBS + ON + CACHE BOOL "" FORCE + ) endif() FetchContent_MakeAvailable(sundials) message(STATUS "SUNDIALS done configuring") else() - enable_language(C) find_package(SUNDIALS REQUIRED) - if (SUNDIALS_VERSION VERSION_LESS 4.0.0) - message(FATAL_ERROR "SUNDIALS_VERSION 4.0.0 or newer is required. Found version ${SUNDIALS_VERSION}.") + if(SUNDIALS_VERSION VERSION_LESS 4.0.0) + message( + FATAL_ERROR + "SUNDIALS_VERSION 4.0.0 or newer is required. Found version ${SUNDIALS_VERSION}." + ) endif() endif() + if(SUNDIALS_DIR) + set(SUNDIALS_ROOT "${SUNDIALS_DIR}") + endif() target_link_libraries(bout++ PUBLIC SUNDIALS::nvecparallel) + if(TARGET SUNDIALS::nvecmanyvector) + target_link_libraries(bout++ PUBLIC SUNDIALS::nvecmanyvector) + set(BOUT_HAS_SUNDIALS_MANYVECTOR ON) + else() + message( + STATUS "SUNDIALS ManyVector support not found; custom N_Vector disabled" + ) + endif() target_link_libraries(bout++ PUBLIC SUNDIALS::cvode) target_link_libraries(bout++ PUBLIC SUNDIALS::ida) target_link_libraries(bout++ PUBLIC SUNDIALS::arkode) - set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${SUNDIALS_cvode_LIBRARY} ${SUNDIALS_ida_LIBRARY} ${SUNDIALS_arkode_LIBRARY} ${SUNDIALS_nvecparallel_LIBRARY}") + set(CONFIG_LDFLAGS + "${CONFIG_LDFLAGS} ${SUNDIALS_cvode_LIBRARY} ${SUNDIALS_ida_LIBRARY} ${SUNDIALS_arkode_LIBRARY} ${SUNDIALS_nvecparallel_LIBRARY}" + ) endif() message(STATUS "SUNDIALS support: ${BOUT_USE_SUNDIALS}") set(BOUT_HAS_SUNDIALS ${BOUT_USE_SUNDIALS}) @@ -312,46 +429,51 @@ set(BOUT_HAS_CVODE ${BOUT_USE_SUNDIALS}) set(BOUT_HAS_IDA ${BOUT_USE_SUNDIALS}) set(ON_OFF_AUTO ON OFF AUTO) -set(BOUT_USE_NLS AUTO CACHE STRING "Enable Native Language Support") +set(BOUT_USE_NLS + AUTO + CACHE STRING "Enable Native Language Support" +) set_property(CACHE BOUT_USE_NLS PROPERTY STRINGS ${ON_OFF_AUTO}) set(BOUT_HAS_GETTEXT OFF) -if (BOUT_USE_NLS) +if(BOUT_USE_NLS) find_package(Gettext) - if (GETTEXT_FOUND) + if(GETTEXT_FOUND) find_package(Intl) - if (Intl_FOUND) - target_link_libraries(bout++ - PUBLIC ${Intl_LIBRARIES}) - target_include_directories(bout++ - PUBLIC ${Intl_INCLUDE_DIRS}) + if(Intl_FOUND) + target_link_libraries(bout++ PUBLIC ${Intl_LIBRARIES}) + target_include_directories(bout++ PUBLIC ${Intl_INCLUDE_DIRS}) set(BOUT_HAS_GETTEXT ON) else() - if (NOT BOUT_USE_NLS STREQUAL "AUTO") - message(FATAL_ERROR "Intl not found but requested!") + if(NOT BOUT_USE_NLS STREQUAL "AUTO") + message(FATAL_ERROR "Intl not found but requested!") endif() endif() else() - if (NOT BOUT_USE_NLS STREQUAL "AUTO") + if(NOT BOUT_USE_NLS STREQUAL "AUTO") message(FATAL_ERROR "GETTEXT not found but requested!") endif() endif() endif() option(BOUT_USE_SCOREP "Enable support for Score-P based instrumentation" OFF) -if (BOUT_USE_SCOREP) - message(STATUS "Score-P support enabled. Please make sure you are calling CMake like so: +if(BOUT_USE_SCOREP) + message( + STATUS + "Score-P support enabled. Please make sure you are calling CMake like so: SCOREP_WRAPPER=off cmake -DCMAKE_C_COMPILER=scorep-mpicc -DCMAKE_CXX_COMPILER=scorep-mpicxx -") +" + ) endif() set(BOUT_HAS_SCOREP ${BOUT_USE_SCOREP}) -option(BOUT_USE_UUID_SYSTEM_GENERATOR "Enable support for using a system UUID generator" ON) -if (BOUT_USE_UUID_SYSTEM_GENERATOR) +option(BOUT_USE_UUID_SYSTEM_GENERATOR + "Enable support for using a system UUID generator" ON +) +if(BOUT_USE_UUID_SYSTEM_GENERATOR) find_package(Libuuid QUIET) - if (Libuuid_FOUND) - target_link_libraries(bout++ - PUBLIC Libuuid::libuuid) + if(Libuuid_FOUND) + target_link_libraries(bout++ PUBLIC Libuuid::libuuid) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${Libuuid_LIBRARIES}") else() message(STATUS "libuuid not found, using fallback UUID generator") @@ -360,3 +482,43 @@ if (BOUT_USE_UUID_SYSTEM_GENERATOR) endif() message(STATUS "UUID_SYSTEM_GENERATOR: ${BOUT_USE_UUID_SYSTEM_GENERATOR}") set(BOUT_HAS_UUID_SYSTEM_GENERATOR ${BOUT_USE_UUID_SYSTEM_GENERATOR}) + +cmake_dependent_option( + BOUT_USE_SYSTEM_CPPTRACE + "Use external installation of cpptrace" + OFF + "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/cpptrace/CMakeLists.txt" + ON +) + +if(BOUT_USE_SYSTEM_CPPTRACE) + message(STATUS "Using external cpptrace") + find_package(cpptrace REQUIRED) + get_target_property( + CPPTRACE_INCLUDE_PATH cpptrace::cpptrace INTERFACE_INCLUDE_DIRECTORIES + ) +else() + message(STATUS "Using cpptrace submodule") + bout_update_submodules() + # Need a fork with some fixes for CMake + set(CPPTRACE_LIBDWARF_REPO + "https://github.com/ZedThree/libdwarf-lite.git" + CACHE STRING "" FORCE + ) + set(CPPTRACE_LIBDWARF_TAG + "ebe10a39afd56b8247de633bfe17666ad50ab95e" + CACHE STRING "" FORCE + ) + add_subdirectory(externalpackages/cpptrace) + if(NOT TARGET cpptrace::cpptrace) + message( + FATAL_ERROR + "cpptrace not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?" + ) + endif() + set(CPPTRACE_INCLUDE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/cpptrace/include" + ) +endif() +set(CONFIG_CFLAGS "${CONFIG_CFLAGS} -I\${CPPTRACE_INCLUDE_PATH}") +target_link_libraries(bout++ PUBLIC cpptrace::cpptrace) diff --git a/cmake/SetupCompilers.cmake b/cmake/SetupCompilers.cmake index 647cb20f75..fbe960b0f0 100644 --- a/cmake/SetupCompilers.cmake +++ b/cmake/SetupCompilers.cmake @@ -1,44 +1,58 @@ - # Note: Currently BOUT++ always needs MPI. This option just determines # whether the find_* routines are used option(BOUT_ENABLE_MPI "Enable MPI support" ON) if(BOUT_ENABLE_MPI) - # This might not be entirely sensible, but helps CMake to find the - # correct MPI, workaround for https://gitlab.kitware.com/cmake/cmake/issues/18895 - find_program(MPIEXEC_EXECUTABLE NAMES mpiexec mpirun) - find_package(MPI REQUIRED) -endif () + # This might not be entirely sensible, but helps CMake to find the + # correct MPI, workaround for https://gitlab.kitware.com/cmake/cmake/issues/18895 + find_program(MPIEXEC_EXECUTABLE NAMES mpiexec mpirun) + find_package(MPI REQUIRED) +endif() set(BOUT_USE_MPI ${BOUT_ENABLE_MPI}) option(BOUT_ENABLE_OPENMP "Enable OpenMP support" OFF) -set(BOUT_OPENMP_SCHEDULE static CACHE STRING "Set OpenMP schedule") -set_property(CACHE BOUT_OPENMP_SCHEDULE PROPERTY STRINGS static dynamic guided auto) -if (BOUT_ENABLE_OPENMP) +set(BOUT_OPENMP_SCHEDULE + static + CACHE STRING "Set OpenMP schedule" +) +set_property( + CACHE BOUT_OPENMP_SCHEDULE PROPERTY STRINGS static dynamic guided auto +) +if(BOUT_ENABLE_OPENMP) find_package(OpenMP REQUIRED) set(possible_openmp_schedules static dynamic guided auto) - if (NOT BOUT_OPENMP_SCHEDULE IN_LIST possible_openmp_schedules) - message(FATAL_ERROR "BOUT_OPENMP_SCHEDULE must be one of ${possible_openmp_schedules}; got ${BOUT_OPENMP_SCHEDULE}") + if(NOT BOUT_OPENMP_SCHEDULE IN_LIST possible_openmp_schedules) + message( + FATAL_ERROR + "BOUT_OPENMP_SCHEDULE must be one of ${possible_openmp_schedules}; got ${BOUT_OPENMP_SCHEDULE}" + ) endif() message(STATUS "OpenMP schedule: ${BOUT_OPENMP_SCHEDULE}") -endif () +endif() set(BOUT_USE_OPENMP ${BOUT_ENABLE_OPENMP}) message(STATUS "Enable OpenMP: ${BOUT_ENABLE_OPENMP}") option(BOUT_ENABLE_CUDA "Enable CUDA support" OFF) -set(CUDA_ARCH "compute_70,code=sm_70" CACHE STRING "CUDA architecture") +set(CUDA_ARCH + "compute_70,code=sm_70" + CACHE STRING "CUDA architecture" +) if(BOUT_ENABLE_CUDA) - # Set specific options for CUDA if enabled - enable_language(CUDA) - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=${CUDA_ARCH} -ccbin ${CMAKE_CXX_COMPILER}") - if (BOUT_ENABLE_RAJA) - # RAJA uses lambda expressions - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr") - endif () + # Set specific options for CUDA if enabled + enable_language(CUDA) + set(CMAKE_CUDA_FLAGS + "${CMAKE_CUDA_FLAGS} -gencode arch=${CUDA_ARCH} -ccbin ${CMAKE_CXX_COMPILER}" + ) + if(BOUT_ENABLE_RAJA) + # RAJA uses lambda expressions + set(CMAKE_CUDA_FLAGS + "${CMAKE_CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr" + ) + endif() -# TODO Ensure openmp flags are not enabled twice! - if (BOUT_ENABLE_OPENMP) - # CMAKE_CUDA_FLAGS does not pass OpenMP_CXX_FLAGS to the host compiler by default - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler ${OpenMP_CXX_FLAGS}") - endif () + # TODO Ensure openmp flags are not enabled twice! + if(BOUT_ENABLE_OPENMP) + # CMAKE_CUDA_FLAGS does not pass OpenMP_CXX_FLAGS to the host compiler by default + set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler ${OpenMP_CXX_FLAGS}") + endif() endif() set(BOUT_HAS_CUDA ${BOUT_ENABLE_CUDA}) diff --git a/cmake_build_defines.hxx.in b/cmake_build_defines.hxx.in index 4d63a01b7d..7fd84c07d7 100644 --- a/cmake_build_defines.hxx.in +++ b/cmake_build_defines.hxx.in @@ -20,8 +20,8 @@ #cmakedefine01 BOUT_HAS_SCOREP #cmakedefine01 BOUT_HAS_SLEPC #cmakedefine01 BOUT_HAS_SUNDIALS +#cmakedefine01 BOUT_HAS_SUNDIALS_MANYVECTOR #cmakedefine01 BOUT_HAS_UUID_SYSTEM_GENERATOR -#cmakedefine01 BOUT_USE_BACKTRACE #cmakedefine01 BOUT_USE_COLOR #cmakedefine01 BOUT_USE_OPENMP #cmakedefine01 BOUT_USE_OUTPUT_DEBUG @@ -39,4 +39,7 @@ // CMake build does not support legacy interface #define BOUT_HAS_LEGACY_NETCDF 0 +// We now always turn this on +#define BOUT_USE_BACKTRACE 1 + #endif // BOUT_BUILD_CONFIG_HXX diff --git a/examples/6field-simple/CMakeLists.txt b/examples/6field-simple/CMakeLists.txt index 6a51327cda..b6584c49e4 100644 --- a/examples/6field-simple/CMakeLists.txt +++ b/examples/6field-simple/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(6field-simple LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(elm_6f +bout_add_example( + elm_6f SOURCES elm_6f.cxx - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc) + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc +) diff --git a/examples/6field-simple/data/BOUT.inp b/examples/6field-simple/data/BOUT.inp index 1e1bacfe6f..951182af06 100644 --- a/examples/6field-simple/data/BOUT.inp +++ b/examples/6field-simple/data/BOUT.inp @@ -61,7 +61,7 @@ fft_measurement_flag = measure # If using FFTW, perform tests to determine fast atol = 1e-08 # absolute tolerance rtol = 1e-05 # relative tolerance -use_precon = false # Use preconditioner: User-supplied or BBD +cvode_precon_method = none # Disable CVODE preconditioning mxstep = 10000 # Number of internal steps between outputs output_step = 1.0 # time between outputs diff --git a/examples/6field-simple/elm_6f.cxx b/examples/6field-simple/elm_6f.cxx index 40e4f7b3b3..7208d89ddf 100644 --- a/examples/6field-simple/elm_6f.cxx +++ b/examples/6field-simple/elm_6f.cxx @@ -16,6 +16,7 @@ #include "bout/msg_stack.hxx" #include "bout/physicsmodel.hxx" #include "bout/sourcex.hxx" +#include "bout/tokamak_coordinates.hxx" #include @@ -235,9 +236,7 @@ class Elm_6f : public PhysicsModel { int damp_width; // Width of inner damped region BoutReal damp_t_const; // Timescale of damping - // Metric coefficients - Field2D Rxy, Bpxy, Btxy, B0, hthe; - Field2D I; // Shear factor + Field2D B0; // Magnetic field BoutReal LnLambda; // ln(Lambda) /// Ion mass @@ -273,8 +272,6 @@ class Elm_6f : public PhysicsModel { * This function implements d2/dy2 where y is the poloidal coordinate theta */ - TRACE("Grad2_par2new( Field3D )"); - Field3D result = D2DY2(f); #if BOUT_USE_TRACK @@ -342,8 +339,6 @@ class Elm_6f : public PhysicsModel { // Parallel gradient along perturbed field-line Field3D Grad_parP(const Field3D& f, CELL_LOC loc = CELL_DEFAULT) { - TRACE("Grad_parP"); - Field3D result; if (parallel_lagrange || parallel_project) { @@ -378,38 +373,10 @@ class Elm_6f : public PhysicsModel { int init(bool restarting) override { bool noshear; - // Get the metric tensor - Coordinates* coord = mesh->getCoordinates(); - output.write("Solving high-beta flute reduced equations\n"); output.write("\tFile : {:s}\n", __FILE__); output.write("\tCompiled: {:s} at {:s}\n", __DATE__, __TIME__); - ////////////////////////////////////////////////////////////// - // Load data from the grid - - // Load 2D profiles - mesh->get(J0, "Jpar0"); // A / m^2 - mesh->get(P0, "pressure"); // Pascals - - // Load curvature term - b0xcv.covariant = false; // Read contravariant components - mesh->get(b0xcv, "bxcv"); // mixed units x: T y: m^-2 z: m^-2 - - // Load metrics - if (mesh->get(Rxy, "Rxy")) { // m - output_error.write("Error: Cannot read Rxy from grid\n"); - return 1; - } - if (mesh->get(Bpxy, "Bpxy")) { // T - output_error.write("Error: Cannot read Bpxy from grid\n"); - return 1; - } - mesh->get(Btxy, "Btxy"); // T - mesh->get(B0, "Bxy"); // T - mesh->get(hthe, "hthe"); // m - mesh->get(I, "sinty"); // m^-2 T^-1 - ////////////////////////////////////////////////////////////// // Read parameters from the options file // @@ -685,19 +652,43 @@ class Elm_6f : public PhysicsModel { phi_curv = options["phi_curv"].doc("Compressional ExB terms").withDefault(true); g = options["gamma"].doc("Ratio of specific heats").withDefault(5.0 / 3.0); - if (!include_curvature) { - b0xcv = 0.0; - } + ////////////////////////////////////////////////////////////// + // Load data from the grid - if (!include_jpar0) { - J0 = 0.0; - } + // Load 2D profiles + mesh->get(P0, "pressure"); // Pascals - if (noshear) { - if (include_curvature) { + // Typical magnetic field + mesh->get(Bbar, "bmag", 1.0); + // Typical length scale + mesh->get(Lbar, "rmag", 1.0); + + // Read, normalise, and set coordinates + const auto tokamak_coords = + bout::set_tokamak_coordinates(*mesh, Lbar, Bbar, noshear or mesh->IncIntShear); + const auto& Rxy = tokamak_coords.Rxy; + const auto& Bpxy = tokamak_coords.Bpxy; + const auto& Btxy = tokamak_coords.Btxy; + const auto& hthe = tokamak_coords.hthe; + const auto& I = tokamak_coords.I_unnormalised; + // Needed in rhs too + B0 = tokamak_coords.Bxy; + + if (include_curvature) { + // Load curvature term + b0xcv.covariant = false; // Read contravariant components + mesh->get(b0xcv, "bxcv"); // mixed units x: T y: m^-2 z: m^-2 + if (noshear) { b0xcv.z += I * b0xcv.x; } - I = 0.0; + } else { + b0xcv = 0.0; + } + + if (include_jpar0) { + mesh->get(J0, "Jpar0"); // A / m^2 + } else { + J0 = 0.0; } ////////////////////////////////////////////////////////////// @@ -705,26 +696,17 @@ class Elm_6f : public PhysicsModel { if (mesh->IncIntShear) { // BOUT-06 style, using d/dx = d/dpsi + I * d/dz - coord->IntShiftTorsion = I; - + mesh->getCoordinates()->IntShiftTorsion = I; } else { // Dimits style, using local coordinate system if (include_curvature) { b0xcv.z += I * b0xcv.x; } - I = 0.0; // I disappears from metric } ////////////////////////////////////////////////////////////// // NORMALISE QUANTITIES - if (mesh->get(Bbar, "bmag")) { // Typical magnetic field - Bbar = 1.0; - } - if (mesh->get(Lbar, "rmag")) { // Typical length scale - Lbar = 1.0; - } - if (mesh->get(Tibar, "Ti_x")) { // Typical ion temperature scale Tibar = 1.0; } @@ -862,14 +844,6 @@ class Elm_6f : public PhysicsModel { b0xcv.y *= Lbar * Lbar; b0xcv.z *= Lbar * Lbar; - Rxy /= Lbar; - Bpxy /= Bbar; - Btxy /= Bbar; - B0 /= Bbar; - hthe /= Lbar; - coord->dx /= Lbar * Lbar * Bbar; - I *= Lbar * Lbar * Bbar; - if ((!T0_fake_prof) && n0_fake_prof) { N0 = N0tanh(n0_height * Nbar, n0_ave * Nbar, n0_width, n0_center, n0_bottom_x); @@ -1049,27 +1023,6 @@ class Elm_6f : public PhysicsModel { dump.add(eta, "eta", 0); } - /**************** CALCULATE METRICS ******************/ - - coord->g11 = SQ(Rxy * Bpxy); - coord->g22 = 1.0 / SQ(hthe); - coord->g33 = SQ(I) * coord->g11 + SQ(B0) / coord->g11; - coord->g12 = 0.0; - coord->g13 = -I * coord->g11; - coord->g23 = -Btxy / (hthe * Bpxy * Rxy); - - coord->J = hthe / Bpxy; - coord->Bxy = B0; - - coord->g_11 = 1.0 / coord->g11 + SQ(I * Rxy); - coord->g_22 = SQ(B0 * hthe / Bpxy); - coord->g_33 = Rxy * Rxy; - coord->g_12 = Btxy * hthe * I * Rxy / Bpxy; - coord->g_13 = I * Rxy * Rxy; - coord->g_23 = Btxy * hthe * Rxy / Bpxy; - - coord->geometry(); // Calculate quantities from metric tensor - // Set B field vector B0vec.covariant = false; diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 849f10e85f..f2444e6bf6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -30,7 +30,6 @@ add_subdirectory(invertable_operator) add_subdirectory(laplacexy/alfven-wave) add_subdirectory(laplacexy/laplace_perp) add_subdirectory(laplacexy/simple) -add_subdirectory(laplacexy/simple-hypre) add_subdirectory(monitor-newapi) add_subdirectory(orszag-tang) add_subdirectory(preconditioning/wave) diff --git a/examples/IMEX/advection-diffusion/CMakeLists.txt b/examples/IMEX/advection-diffusion/CMakeLists.txt index 334d48d767..f2008f3479 100644 --- a/examples/IMEX/advection-diffusion/CMakeLists.txt +++ b/examples/IMEX/advection-diffusion/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(advection-diffusion LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/IMEX/advection-reaction/CMakeLists.txt b/examples/IMEX/advection-reaction/CMakeLists.txt index 03e8686371..d89ff10a78 100644 --- a/examples/IMEX/advection-reaction/CMakeLists.txt +++ b/examples/IMEX/advection-reaction/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(advection-reaction LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(split_operator +bout_add_example( + split_operator SOURCES split_operator.cxx - EXTRA_FILES simple_xz.nc) + EXTRA_FILES simple_xz.nc +) diff --git a/examples/IMEX/diffusion-nl/CMakeLists.txt b/examples/IMEX/diffusion-nl/CMakeLists.txt index 664d16e042..73c7250bb5 100644 --- a/examples/IMEX/diffusion-nl/CMakeLists.txt +++ b/examples/IMEX/diffusion-nl/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(diffusion-nl LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/IMEX/diffusion-nl/README.md b/examples/IMEX/diffusion-nl/README.md index 3654d484ee..8356955b95 100644 --- a/examples/IMEX/diffusion-nl/README.md +++ b/examples/IMEX/diffusion-nl/README.md @@ -32,7 +32,7 @@ Preconditioning The preconditioner can be enabled by adding another flag (or setting in BOUT.inp) - $ ./diffusion-nl solver:use_precon=true + $ ./diffusion-nl solver:cvode_precon_method=user which then greatly reduces the number of iterations needed by CVODE: @@ -59,4 +59,3 @@ can be calculated using finite differences. This uses coloring to improve effici 1.000e+00 2 26 3.10e-03 31.9 0.0 5.9 7.8 54.4 2.000e+00 2 23 2.24e-03 35.7 0.0 7.0 7.9 49.4 3.000e+00 2 18 2.04e-03 35.0 0.0 6.2 8.5 50.4 - diff --git a/examples/IMEX/drift-wave-constraint/CMakeLists.txt b/examples/IMEX/drift-wave-constraint/CMakeLists.txt index 5680b5367e..b72396d2f0 100644 --- a/examples/IMEX/drift-wave-constraint/CMakeLists.txt +++ b/examples/IMEX/drift-wave-constraint/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(drift-wave-constraint LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/IMEX/drift-wave/CMakeLists.txt b/examples/IMEX/drift-wave/CMakeLists.txt index e3e2a1b8ee..44fbbd7b0f 100644 --- a/examples/IMEX/drift-wave/CMakeLists.txt +++ b/examples/IMEX/drift-wave/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(drift-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/blob2d-laplacexz/CMakeLists.txt b/examples/blob2d-laplacexz/CMakeLists.txt index 17f9ebe97a..855d86af70 100644 --- a/examples/blob2d-laplacexz/CMakeLists.txt +++ b/examples/blob2d-laplacexz/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(blob2d-laplacexz LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/blob2d-outerloop/CMakeLists.txt b/examples/blob2d-outerloop/CMakeLists.txt index cd7187ee3f..e991b45d51 100644 --- a/examples/blob2d-outerloop/CMakeLists.txt +++ b/examples/blob2d-outerloop/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(blob2d-outerloop LANGUAGES CXX C) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/blob2d/CMakeLists.txt b/examples/blob2d/CMakeLists.txt index a4772874d9..f3f93f0246 100644 --- a/examples/blob2d/CMakeLists.txt +++ b/examples/blob2d/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(blob2d LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(blob2d +bout_add_example( + blob2d SOURCES blob2d.cxx DATA_DIRS delta_0.25 delta_1 delta_10 two_blobs data - EXTRA_FILES blob_velocity.py) + EXTRA_FILES blob_velocity.py +) diff --git a/examples/blob2d/blob_velocity.py b/examples/blob2d/blob_velocity.py index d044946320..6de0937d0f 100644 --- a/examples/blob2d/blob_velocity.py +++ b/examples/blob2d/blob_velocity.py @@ -3,96 +3,100 @@ import pickle try: - from past.utils import old_div + from past.utils import old_div except ImportError: - def old_div(a,b): - return a/b - -def blob_velocity(n,**kwargs): - - from boututils import calculus as Calc - # Calculate blob velocity in normalized time and normalized grid spacing - # - # Input: Blob density as a 3D vector in the form n[t,x,z] where t is time and x,z are the perpendicular spatial coordinates - # - # Keywords: - # - # type='peak' -> Calculate velocity of the peak density - # type='COM' -> Calculate centre of mass velocity - # Index=True -> return indices used to create velocity - # - # Default: Peak velocity with no index returning - - size = n.shape - - try: - v_type = kwargs['type'] - except: - v_type = 'peak' #Default to peak velocity calculation - try: - return_index = kwargs['Index'] - except: - return_index = False #Default to no index returning - - - if v_type == 'peak': - x = np.zeros(size[0]) - z = np.zeros(size[0]) - for i in np.arange(size[0]): - nmax,nmin = np.amax((n[i,:,:])),np.amin((n[i,:,:])) - xpos,zpos = np.where(n[i,:,:]==nmax) - x[i] = xpos[0] - z[i] = zpos[0] - - if v_type == 'COM': - x = np.zeros(size[0]) - z = np.zeros(size[0]) - for i in np.arange(size[0]): - data = n[i,:,:] - n[0,0,0] #use corner cell rather than nmin - ntot = np.sum(data[:,:]) - - z[i] = old_div(np.sum(np.sum(data[:,:],axis=0)*(np.arange(size[2]))),ntot) - x[i] = old_div(np.sum(np.sum(data[:,:],axis=1)*(np.arange(size[1]))),ntot) - - vx = Calc.deriv(x) - vz = Calc.deriv(z) - - if return_index: - return vx,vz,x,z - else: - return vx,vz - - - -data='data' + def old_div(a, b): + return a / b + + +def blob_velocity(n, **kwargs): + from boututils import calculus as Calc + # Calculate blob velocity in normalized time and normalized grid spacing + # + # Input: Blob density as a 3D vector in the form n[t,x,z] where t is time and x,z are the perpendicular spatial coordinates + # + # Keywords: + # + # type='peak' -> Calculate velocity of the peak density + # type='COM' -> Calculate centre of mass velocity + # Index=True -> return indices used to create velocity + # + # Default: Peak velocity with no index returning + + size = n.shape + + try: + v_type = kwargs["type"] + except: + v_type = "peak" # Default to peak velocity calculation + try: + return_index = kwargs["Index"] + except: + return_index = False # Default to no index returning + + if v_type == "peak": + x = np.zeros(size[0]) + z = np.zeros(size[0]) + for i in np.arange(size[0]): + nmax, nmin = np.amax((n[i, :, :])), np.amin((n[i, :, :])) + xpos, zpos = np.where(n[i, :, :] == nmax) + x[i] = xpos[0] + z[i] = zpos[0] + + if v_type == "COM": + x = np.zeros(size[0]) + z = np.zeros(size[0]) + for i in np.arange(size[0]): + data = n[i, :, :] - n[0, 0, 0] # use corner cell rather than nmin + ntot = np.sum(data[:, :]) + + z[i] = old_div( + np.sum(np.sum(data[:, :], axis=0) * (np.arange(size[2]))), ntot + ) + x[i] = old_div( + np.sum(np.sum(data[:, :], axis=1) * (np.arange(size[1]))), ntot + ) + + vx = Calc.deriv(x) + vz = Calc.deriv(z) + + if return_index: + return vx, vz, x, z + else: + return vx, vz + + +data = "data" if True: - import sys - if len(sys.argv) > 1: - data=sys.argv[1] + import sys + + if len(sys.argv) > 1: + data = sys.argv[1] -n = collect('n', path=data, info=False) +n = collect("n", path=data, info=False) -vx,vy,xx,yy = blob_velocity(n[:,:,0,:],type='COM',Index=True) +vx, vy, xx, yy = blob_velocity(n[:, :, 0, :], type="COM", Index=True) -f = open('Velocity.dat','wb') -pickle.dump(vx,f) +f = open("Velocity.dat", "wb") +pickle.dump(vx, f) f.close() -f = open('Position.dat','wb') -pickle.dump(xx,f) +f = open("Position.dat", "wb") +pickle.dump(xx, f) f.close() -f = open('Velocity.dat','rb') +f = open("Velocity.dat", "rb") vx = pickle.load(f) f.close() try: - import matplotlib.pyplot as plt - plt.plot(vx) - plt.show() + import matplotlib.pyplot as plt + + plt.plot(vx) + plt.show() except ImportError: - pass + pass diff --git a/examples/boundary-conditions/advection/CMakeLists.txt b/examples/boundary-conditions/advection/CMakeLists.txt index a4ab73a24d..edd55d183a 100644 --- a/examples/boundary-conditions/advection/CMakeLists.txt +++ b/examples/boundary-conditions/advection/CMakeLists.txt @@ -2,13 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(advection LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(advection +bout_add_example( + advection SOURCES advection.cxx - DATA_DIRS central-dirichlet - central-free - central-free-o3 - upwind) + DATA_DIRS central-dirichlet central-free central-free-o3 upwind +) diff --git a/examples/boutpp/CMakeLists.txt b/examples/boutpp/CMakeLists.txt index e46a7ae990..1cc5c9619b 100644 --- a/examples/boutpp/CMakeLists.txt +++ b/examples/boutpp/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.13) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/boutpp/simulation.py b/examples/boutpp/simulation.py index 1b57ff8b33..9948bd9b03 100755 --- a/examples/boutpp/simulation.py +++ b/examples/boutpp/simulation.py @@ -3,12 +3,13 @@ bc.init("mesh:n=48") + class Model(bc.PhysicsModel): - def init(self,restart): + def init(self, restart): self.dens = bc.create3D("sin(x)") self.solve_for(n=self.dens) - def rhs(self,time): + def rhs(self, time): self.dens.ddt(bc.DDX(self.dens)) diff --git a/examples/conducting-wall-mode/CMakeLists.txt b/examples/conducting-wall-mode/CMakeLists.txt index 857a22038e..0e000c1ba8 100644 --- a/examples/conducting-wall-mode/CMakeLists.txt +++ b/examples/conducting-wall-mode/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(conducting-wall-mode LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(conducting-wall-mode +bout_add_example( + conducting-wall-mode SOURCES cwm.cxx - EXTRA_FILES cwm_grid.nc) + EXTRA_FILES cwm_grid.nc +) diff --git a/examples/conducting-wall-mode/cwm.cxx b/examples/conducting-wall-mode/cwm.cxx index 6ad23033ee..b302b5dfb3 100644 --- a/examples/conducting-wall-mode/cwm.cxx +++ b/examples/conducting-wall-mode/cwm.cxx @@ -4,12 +4,14 @@ * Mode discoverd by H.L. Berk et. al. 1993 * Model version in the code created by M. Umansky and J. Myra. *******************************************************************************/ -#include #include #include #include #include +#include +#include +#include class CWM : public PhysicsModel { private: @@ -28,9 +30,6 @@ class CWM : public PhysicsModel { // Phi boundary conditions Field3D dphi_bc_ydown, dphi_bc_yup; - // Metric coefficients - Field2D Rxy, Bpxy, Btxy, hthe, Zxy; - // parameters BoutReal Te_x, Ni_x, Vi_x, bmag, rho_s, fmei, AA, ZZ; BoutReal lambda_ei; @@ -54,8 +53,6 @@ class CWM : public PhysicsModel { std::unique_ptr phiSolver{nullptr}; int init(bool UNUSED(restarting)) override { - Field2D I; // Shear factor - /************* LOAD DATA FROM GRID FILE ****************/ // Load 2D profiles (set to zero if not found) @@ -63,11 +60,6 @@ class CWM : public PhysicsModel { coord = mesh->getCoordinates(); - // Load metrics - GRID_LOAD(Rxy, Zxy, Bpxy, Btxy, hthe); - mesh->get(coord->dx, "dpsi"); - mesh->get(I, "sinty"); - // Load normalisation values GRID_LOAD(Te_x, Ni_x, bmag); @@ -93,13 +85,11 @@ class CWM : public PhysicsModel { /************* SHIFTED RADIAL COORDINATES ************/ // Check type of parallel transform - std::string ptstr = - Options::root()["mesh"]["paralleltransform"]["type"].withDefault( - "identity"); + const auto ptstr = + Options::root()["mesh"]["paralleltransform"]["type"].withDefault("identity"); - if (lowercase(ptstr) == "shifted") { - ShearFactor = 0.0; // I disappears from metric - } + // I disappears from metric + const bool noshear = (lowercase(ptstr) == "shifted"); /************** CALCULATE PARAMETERS *****************/ @@ -132,39 +122,19 @@ class CWM : public PhysicsModel { Ni0 /= Ni_x / 1.0e14; Te0 /= Te_x; - // Normalise geometry - Rxy /= rho_s; - hthe /= rho_s; - I *= rho_s * rho_s * (bmag / 1e4) * ShearFactor; - coord->dx /= rho_s * rho_s * (bmag / 1e4); - - // Normalise magnetic field - Bpxy /= (bmag / 1.e4); - Btxy /= (bmag / 1.e4); - coord->Bxy /= (bmag / 1.e4); - // Set nu nu = nu_hat * Ni0 / pow(Te0, 1.5); /**************** CALCULATE METRICS ******************/ - coord->g11 = SQ(Rxy * Bpxy); - coord->g22 = 1.0 / SQ(hthe); - coord->g33 = SQ(I) * coord->g11 + SQ(coord->Bxy) / coord->g11; - coord->g12 = 0.0; - coord->g13 = -I * coord->g11; - coord->g23 = -Btxy / (hthe * Bpxy * Rxy); - - coord->J = hthe / Bpxy; - - coord->g_11 = 1.0 / coord->g11 + SQ(I * Rxy); - coord->g_22 = SQ(coord->Bxy * hthe / Bpxy); - coord->g_33 = Rxy * Rxy; - coord->g_12 = Btxy * hthe * I * Rxy / Bpxy; - coord->g_13 = I * Rxy * Rxy; - coord->g_23 = Btxy * hthe * Rxy / Bpxy; - - coord->geometry(); + // Read, normalise, and set coordinates + const auto tokamak_coords = + bout::set_tokamak_coordinates(*mesh, rho_s, bmag / 1e4, noshear); + auto Rxy = tokamak_coords.Rxy; + auto Zxy = tokamak_coords.Zxy; + auto Bpxy = tokamak_coords.Bpxy; + auto Btxy = tokamak_coords.Btxy; + auto hthe = tokamak_coords.hthe; /**************** SET EVOLVING VARIABLES *************/ diff --git a/examples/conduction-snb/CMakeLists.txt b/examples/conduction-snb/CMakeLists.txt index 45072dbe59..3de25718d1 100644 --- a/examples/conduction-snb/CMakeLists.txt +++ b/examples/conduction-snb/CMakeLists.txt @@ -2,11 +2,20 @@ cmake_minimum_required(VERSION 3.13) project(conduction-snb LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(conduction-snb +bout_add_example( + conduction-snb SOURCES conduction-snb.cxx - EXTRA_FILES fit_temperature.py sinusoid.py snb.csv spitzer-harm.csv step.py temperature.csv vfp.csv - DATA_DIRS data step) + EXTRA_FILES + fit_temperature.py + sinusoid.py + snb.csv + spitzer-harm.csv + step.py + temperature.csv + vfp.csv + DATA_DIRS data step +) diff --git a/examples/conduction-snb/fit_temperature.py b/examples/conduction-snb/fit_temperature.py index 1e7f3ddf94..81faa9cf93 100644 --- a/examples/conduction-snb/fit_temperature.py +++ b/examples/conduction-snb/fit_temperature.py @@ -3,23 +3,31 @@ import matplotlib.pyplot as plt Te_ref = np.loadtxt("temperature.csv", delimiter=",") -Te_ref[:,0] *= 1e-4 # Convert X axis to m +Te_ref[:, 0] *= 1e-4 # Convert X axis to m + def te_function(ypos, mid, wwid, w0, w1, w2, Tmax, Tmin, clip=False): - width = w0 + ((ypos - mid)*w1 + (ypos - mid)**2 * w2) * np.exp(-((ypos - mid)/wwid)**2) + width = w0 + ((ypos - mid) * w1 + (ypos - mid) ** 2 * w2) * np.exp( + -(((ypos - mid) / wwid) ** 2) + ) if clip: width = np.clip(width, 1e-10, None) - return Tmax - 0.5 * (1 + np.tanh((ypos - mid)/width)) * (Tmax - Tmin) + return Tmax - 0.5 * (1 + np.tanh((ypos - mid) / width)) * (Tmax - Tmin) + -popt, pcov = optimize.curve_fit(te_function, Te_ref[:,0], Te_ref[:,1], - p0 = [2.2e-4, 1e-4, 1e-4, 0.0, 0.0, 0.960, 0.190]) +popt, pcov = optimize.curve_fit( + te_function, + Te_ref[:, 0], + Te_ref[:, 1], + p0=[2.2e-4, 1e-4, 1e-4, 0.0, 0.0, 0.960, 0.190], +) print(popt) -xfit = np.linspace(Te_ref[0,0], Te_ref[-1,0], 100) +xfit = np.linspace(Te_ref[0, 0], Te_ref[-1, 0], 100) -plt.plot(xfit, te_function(xfit, *popt, clip=True), '-k') -plt.plot(Te_ref[:,0], Te_ref[:,1], 'or') +plt.plot(xfit, te_function(xfit, *popt, clip=True), "-k") +plt.plot(Te_ref[:, 0], Te_ref[:, 1], "or") plt.show() diff --git a/examples/conduction-snb/sinusoid.py b/examples/conduction-snb/sinusoid.py index 10c81923a3..439530383e 100644 --- a/examples/conduction-snb/sinusoid.py +++ b/examples/conduction-snb/sinusoid.py @@ -11,7 +11,7 @@ build_and_log("Sinusoidal SNB") # Electron temperature in eV -Telist = 10 ** np.linspace(0,3,20) +Telist = 10 ** np.linspace(0, 3, 20) # Electron density in m^-3 Ne = 1e20 @@ -19,29 +19,30 @@ # Length of the domain in m length = 1.0 -c = 299792458 -mu0 = 4.e-7*np.pi -e0 = 1/(c*c*mu0) +c = 299792458 +mu0 = 4.0e-7 * np.pi +e0 = 1 / (c * c * mu0) qe = 1.602176634e-19 me = 9.10938356e-31 -thermal_speed = np.sqrt(2.*qe * Telist / me) -Y = (qe**2 / (e0 * me))**2 / (4 * np.pi) +thermal_speed = np.sqrt(2.0 * qe * Telist / me) +Y = (qe**2 / (e0 * me)) ** 2 / (4 * np.pi) coulomb_log = 6.6 - 0.5 * np.log(Ne * 1e-20) + 1.5 * np.log(Telist) -lambda_ee_T = thermal_speed**4 / (Y * Ne * coulomb_log) +lambda_ee_T = thermal_speed**4 / (Y * Ne * coulomb_log) beta_max_list = [5, 10, 20, 40] -colors = ['k','b','g','r'] +colors = ["k", "b", "g", "r"] ngroups_list = [20, 40, 80] -syms = ['x', 'o', 'D'] +syms = ["x", "o", "D"] for beta_max, color in zip(beta_max_list, colors): for ngroups, sym in zip(ngroups_list, syms): - flux_ratio = [] for Te in Telist: - cmd = "./conduction-snb \"Te={0}+0.01*sin(y)\" Ne={1} mesh:length={2} snb:beta_max={3} snb:ngroups={4}".format(Te, Ne, length, beta_max, ngroups) + cmd = './conduction-snb "Te={0}+0.01*sin(y)" Ne={1} mesh:length={2} snb:beta_max={3} snb:ngroups={4}'.format( + Te, Ne, length, beta_max, ngroups + ) # Run the case s, out = launch_safe(cmd, nproc=1, mthread=1, pipe=True) @@ -54,7 +55,12 @@ flux_ratio.append(div_q[ind] / div_q_SH[ind]) - plt.plot(lambda_ee_T / length, flux_ratio, '-'+sym+color, label=r"$\beta_{{max}}={0}, N_g={1}$".format(beta_max,ngroups)) + plt.plot( + lambda_ee_T / length, + flux_ratio, + "-" + sym + color, + label=r"$\beta_{{max}}={0}, N_g={1}$".format(beta_max, ngroups), + ) plt.legend() plt.xlabel(r"$\lambda_{ee,T} / L$") diff --git a/examples/conduction-snb/step.py b/examples/conduction-snb/step.py index 1f8933e66b..63a13149f4 100644 --- a/examples/conduction-snb/step.py +++ b/examples/conduction-snb/step.py @@ -3,7 +3,7 @@ # # Uses a step in the temperature, intended for comparison to VFP results -length = 6e-4 # Domain length in m +length = 6e-4 # Domain length in m qe = 1.602176634e-19 @@ -40,37 +40,37 @@ # Read reference values Te_ref = np.loadtxt("temperature.csv", delimiter=",") -Te_ref[:,0] *= 1e-4 # Convert X axis to m +Te_ref[:, 0] *= 1e-4 # Convert X axis to m SH_ref = np.loadtxt("spitzer-harm.csv", delimiter=",") -SH_ref[:,0] *= 1e-4 +SH_ref[:, 0] *= 1e-4 SNB_ref = np.loadtxt("snb.csv", delimiter=",") -SNB_ref[:,0] *= 1e-4 +SNB_ref[:, 0] *= 1e-4 VFP_ref = np.loadtxt("vfp.csv", delimiter=",") -VFP_ref[:,0] *= 1e-4 +VFP_ref[:, 0] *= 1e-4 ######################################### fig, ax1 = plt.subplots() -color='tab:red' +color = "tab:red" ax1.plot(position, Te * 1e-3, color=color, label="Te") -ax1.plot(Te_ref[:,0], Te_ref[:,1], color=color, marker="o", label="Reference Te") +ax1.plot(Te_ref[:, 0], Te_ref[:, 1], color=color, marker="o", label="Reference Te") ax1.set_xlabel("position [m]") ax1.set_ylabel("Electron temperature [keV]", color=color) -ax1.set_ylim(0,1) -ax1.tick_params(axis='y', colors=color) +ax1.set_ylim(0, 1) +ax1.tick_params(axis="y", colors=color) ax2 = ax1.twinx() -ax2.plot(position, q_SH * 1e-4, '-k', label="Spitzer-Harm") -ax2.plot(SH_ref[:,0], SH_ref[:,1], '--k', label="Reference SH") +ax2.plot(position, q_SH * 1e-4, "-k", label="Spitzer-Harm") +ax2.plot(SH_ref[:, 0], SH_ref[:, 1], "--k", label="Reference SH") -ax2.plot(position, q * 1e-4, '-b', label="SNB") -ax2.plot(SNB_ref[:,0], SNB_ref[:,1], '--b', label="Reference SNB") +ax2.plot(position, q * 1e-4, "-b", label="SNB") +ax2.plot(SNB_ref[:, 0], SNB_ref[:, 1], "--b", label="Reference SNB") -ax2.plot(VFP_ref[:,0], VFP_ref[:,1], '--g', label="Reference VFP") +ax2.plot(VFP_ref[:, 0], VFP_ref[:, 1], "--g", label="Reference VFP") ax2.set_ylabel("Heat flux W/cm^2") ax2.set_ylim(bottom=0.0) @@ -78,8 +78,7 @@ plt.legend() fig.tight_layout() -plt.xlim(0,3.5e-4) +plt.xlim(0, 3.5e-4) plt.savefig("snb-step.png") plt.show() - diff --git a/examples/conduction/CMakeLists.txt b/examples/conduction/CMakeLists.txt index f26b838621..b81164f614 100644 --- a/examples/conduction/CMakeLists.txt +++ b/examples/conduction/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(conduction LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(conduction +bout_add_example( + conduction SOURCES conduction.cxx DATA_DIRS data fromfile - EXTRA_FILES generate.py) + EXTRA_FILES generate.py +) diff --git a/examples/conduction/generate.py b/examples/conduction/generate.py index 7138026898..4a2a2acfc0 100755 --- a/examples/conduction/generate.py +++ b/examples/conduction/generate.py @@ -4,9 +4,9 @@ # Generate an input mesh # -from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries +from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries -nx = 5 # Minimum is 5: 2 boundary, one evolved +nx = 5 # Minimum is 5: 2 boundary, one evolved ny = 64 # Minimum 5. Should be divisible by number of processors (so powers of 2 nice) f = DataFile() diff --git a/examples/constraints/alfven-wave/CMakeLists.txt b/examples/constraints/alfven-wave/CMakeLists.txt index a95ace4086..375415ab3c 100644 --- a/examples/constraints/alfven-wave/CMakeLists.txt +++ b/examples/constraints/alfven-wave/CMakeLists.txt @@ -2,12 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(constraints-alfven-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(constraints-alfven-wave +bout_add_example( + constraints-alfven-wave SOURCES alfven.cxx DATA_DIRS cbm18 data - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc d3d_119919.nc) - + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc d3d_119919.nc +) diff --git a/examples/constraints/alfven-wave/alfven.cxx b/examples/constraints/alfven-wave/alfven.cxx index 3e643331a1..85428fdc85 100644 --- a/examples/constraints/alfven-wave/alfven.cxx +++ b/examples/constraints/alfven-wave/alfven.cxx @@ -1,8 +1,9 @@ - #include #include #include #include +#include +#include /// Fundamental constants const BoutReal PI = 3.14159265; @@ -129,12 +130,12 @@ class Alfven : public PhysicsModel { } /*! - * Preconditioner. This inverts the operator (1 - gamma*J) + * Preconditioner. This inverts the operator (1 - gamma*J) * where J is the Jacobian of the system * * The system state at time t is stored as usual * whilst the vector to be inverted is in ddt(f) - * + * * Inputs * ------ * @@ -146,7 +147,7 @@ class Alfven : public PhysicsModel { * * Output * ------ - * + * * ddt(f) = Result of the inversion */ int precon(BoutReal, BoutReal, BoutReal) { @@ -159,65 +160,14 @@ class Alfven : public PhysicsModel { } void LoadMetric(BoutReal Lnorm, BoutReal Bnorm) { - // Load metric coefficients from the mesh - Field2D Rxy, Bpxy, Btxy, hthe, sinty; - GRID_LOAD5(Rxy, Bpxy, Btxy, hthe, sinty); // Load metrics - - // Get the coordinates object - Coordinates* coord = mesh->getCoordinates(); - - // Checking for dpsi and qinty used in BOUT grids - Field2D dx; - if (!mesh->get(dx, "dpsi")) { - output << "\tUsing dpsi as the x grid spacing\n"; - coord->dx = dx; // Only use dpsi if found - } else { - // dx will have been read already from the grid - output << "\tUsing dx as the x grid spacing\n"; - } - - Rxy /= Lnorm; - hthe /= Lnorm; - sinty *= SQ(Lnorm) * Bnorm; - coord->dx /= SQ(Lnorm) * Bnorm; - - Bpxy /= Bnorm; - Btxy /= Bnorm; - coord->Bxy /= Bnorm; - // Check type of parallel transform - std::string ptstr = + const auto ptstr = Options::root()["mesh"]["paralleltransform"]["type"].withDefault("identity"); - if (lowercase(ptstr) == "shifted") { - // Using shifted metric method - sinty = 0.0; // I disappears from metric - } - - BoutReal sbp = 1.0; // Sign of Bp - if (min(Bpxy, true) < 0.0) { - sbp = -1.0; - } - - // Calculate metric components - - coord->g11 = SQ(Rxy * Bpxy); - coord->g22 = 1.0 / SQ(hthe); - coord->g33 = SQ(sinty) * coord->g11 + SQ(coord->Bxy) / coord->g11; - coord->g12 = 0.0; - coord->g13 = -sinty * coord->g11; - coord->g23 = -sbp * Btxy / (hthe * Bpxy * Rxy); - - coord->J = hthe / Bpxy; - - coord->g_11 = 1.0 / coord->g11 + SQ(sinty * Rxy); - coord->g_22 = SQ(coord->Bxy * hthe / Bpxy); - coord->g_33 = Rxy * Rxy; - coord->g_12 = sbp * Btxy * hthe * sinty * Rxy / Bpxy; - coord->g_13 = sinty * Rxy * Rxy; - coord->g_23 = sbp * Btxy * hthe * Rxy / Bpxy; - - coord->geometry(); + // I disappears from metric + const bool noshear = (lowercase(ptstr) == "shifted"); + // Read, normalise, and set coordinates + bout::set_tokamak_coordinates(*mesh, Lnorm, Bnorm, noshear); } }; diff --git a/examples/constraints/laplace-dae/CMakeLists.txt b/examples/constraints/laplace-dae/CMakeLists.txt index e487bd6a0a..d515fc3515 100644 --- a/examples/constraints/laplace-dae/CMakeLists.txt +++ b/examples/constraints/laplace-dae/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(constraints-laplace-dae LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(constraints-laplace-dae +bout_add_example( + constraints-laplace-dae SOURCES laplace_dae.cxx - EXTRA_FILES simple_xz.nc) + EXTRA_FILES simple_xz.nc +) diff --git a/examples/constraints/laplace-dae/data/BOUT.inp b/examples/constraints/laplace-dae/data/BOUT.inp index ea7c8d1765..ccc9a647ee 100644 --- a/examples/constraints/laplace-dae/data/BOUT.inp +++ b/examples/constraints/laplace-dae/data/BOUT.inp @@ -54,7 +54,7 @@ mxstep = 50000 #type=splitrk #timestep=1e-4 -use_precon = true +cvode_precon_method = user output_step = 0.01 # time between outputs # Settings for split operator test case diff --git a/examples/dalf3/CMakeLists.txt b/examples/dalf3/CMakeLists.txt index 5f5b3d701d..0b6f8461f5 100644 --- a/examples/dalf3/CMakeLists.txt +++ b/examples/dalf3/CMakeLists.txt @@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(dalf3 LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() - -bout_add_example(dalf3 +bout_add_example( + dalf3 SOURCES dalf3.cxx - EXTRA_FILES cbm18_8_y064_x516_090309.nc) + EXTRA_FILES cbm18_8_y064_x516_090309.nc +) diff --git a/examples/dalf3/dalf3.cxx b/examples/dalf3/dalf3.cxx index 493e8f5e45..4cf0e811bf 100644 --- a/examples/dalf3/dalf3.cxx +++ b/examples/dalf3/dalf3.cxx @@ -1,6 +1,6 @@ /**************************************************************** * DALF3 model - * + * * Four-field model for electron pressure, vorticity, A|| and * parallel velocity * @@ -18,17 +18,18 @@ * ****************************************************************/ -#include - +#include #include #include #include +#include +#include #include + +#include #include #include -#include - // Constants const BoutReal MU0 = 4.0e-7 * PI; const BoutReal Charge = SI::qe; // electron charge e (C) @@ -71,8 +72,6 @@ class DALF3 : public PhysicsModel { BoutReal viscosity, hyper_viscosity; - bool smooth_separatrix; - FieldGroup comms; std::unique_ptr phiSolver{nullptr}; // Laplacian solver in X-Z @@ -99,22 +98,9 @@ class DALF3 : public PhysicsModel { b0xcv.covariant = false; // Read contravariant components mesh->get(b0xcv, "bxcv"); // mixed units x: T y: m^-2 z: m^-2 - // Metric coefficients - Field2D Rxy, Bpxy, Btxy, hthe; - Field2D I; // Shear factor - - if (mesh->get(Rxy, "Rxy")) { // m - output_error.write("Error: Cannot read Rxy from grid\n"); - return 1; - } - if (mesh->get(Bpxy, "Bpxy")) { // T - output_error.write("Error: Cannot read Bpxy from grid\n"); - return 1; - } - mesh->get(Btxy, "Btxy"); // T - mesh->get(B0, "Bxy"); // T - mesh->get(hthe, "hthe"); // m - mesh->get(I, "sinty"); // m^-2 T^-1 + // We need to load this in, despite using `set_tokamak_coordinates`, because + // the both length and B normalisation depend on it + mesh->get(B0, "Bxy"); // T ////////////////////////////////////////////////////////////// // Options @@ -132,7 +118,6 @@ class DALF3 : public PhysicsModel { viscosity = options["viscosity"].withDefault(-1.0); hyper_viscosity = options["hyper_viscosity"].withDefault(-1.0); viscosity_par = options["viscosity_par"].withDefault(-1.0); - smooth_separatrix = options["smooth_separatrix"].withDefault(false); filter_z = options["filter_z"].withDefault(false); @@ -166,20 +151,6 @@ class DALF3 : public PhysicsModel { return 1; } - Coordinates* coord = mesh->getCoordinates(); - - // SHIFTED RADIAL COORDINATES - - // Check type of parallel transform - std::string ptstr = - Options::root()["mesh"]["paralleltransform"]["type"].withDefault("identity"); - - if (lowercase(ptstr) == "shifted") { - // Dimits style, using local coordinate system - b0xcv.z += I * b0xcv.x; - I = 0.0; // I disappears from metric - } - /////////////////////////////////////////////////// // Normalisation @@ -215,9 +186,7 @@ class DALF3 : public PhysicsModel { } else { eta = 0.51 * 1.03e-4 * 20. * pow(Te0, -1.5); } - if (mul_resist < 0.0) { - mul_resist = 0.0; - } + mul_resist = std::max(mul_resist, 0.0); eta *= mul_resist; // Plasma quantities @@ -231,41 +200,31 @@ class DALF3 : public PhysicsModel { hyper_viscosity /= wci * SQ(SQ(rho_s)); viscosity_par /= wci * SQ(rho_s); - b0xcv.x /= Bnorm; - b0xcv.y *= rho_s * rho_s; - b0xcv.z *= rho_s * rho_s; - - // Metrics - Rxy /= rho_s; - hthe /= rho_s; - I *= rho_s * rho_s * Bnorm; - Bpxy /= Bnorm; - Btxy /= Bnorm; - B0 /= Bnorm; - - coord->dx /= rho_s * rho_s * Bnorm; - /////////////////////////////////////////////////// // CALCULATE METRICS - coord->g11 = SQ(Rxy * Bpxy); - coord->g22 = 1.0 / SQ(hthe); - coord->g33 = SQ(I) * coord->g11 + SQ(B0) / coord->g11; - coord->g12 = 0.0; - coord->g13 = -I * coord->g11; - coord->g23 = -Btxy / (hthe * Bpxy * Rxy); + // Check type of parallel transform + const auto ptstr = + Options::root()["mesh"]["paralleltransform"]["type"].withDefault("identity"); + + // I disappears from metric + const bool noshear = (lowercase(ptstr) == "shifted"); - coord->J = hthe / Bpxy; - coord->Bxy = B0; + // Read, normalise, and set coordinates + const auto tokamak_coords = + bout::set_tokamak_coordinates(*mesh, rho_s, Bnorm, noshear); + // Use normalised B0 + B0 = tokamak_coords.Bxy; - coord->g_11 = 1.0 / coord->g11 + SQ(I * Rxy); - coord->g_22 = SQ(B0 * hthe / Bpxy); - coord->g_33 = Rxy * Rxy; - coord->g_12 = Btxy * hthe * I * Rxy / Bpxy; - coord->g_13 = I * Rxy * Rxy; - coord->g_23 = Btxy * hthe * Rxy / Bpxy; + // SHIFTED RADIAL COORDINATES + if (noshear) { + // Dimits style, using local coordinate system + b0xcv.z += tokamak_coords.I_unnormalised * b0xcv.x; + } - coord->geometry(); // Calculate quantities from metric tensor + b0xcv.x /= Bnorm; + b0xcv.y *= rho_s * rho_s; + b0xcv.z *= rho_s * rho_s; SOLVE_FOR3(Vort, Pe, Vpar); comms.add(Vort, Pe, Vpar); @@ -300,7 +259,7 @@ class DALF3 : public PhysicsModel { // LaplaceXY for n=0 solve if (split_n0) { // Create an XY solver for n=0 component - laplacexy = std::make_unique(mesh); + laplacexy = LaplaceXY::create(mesh); phi2D = 0.0; // Starting guess } @@ -466,11 +425,6 @@ class DALF3 : public PhysicsModel { ddt(Pe) = -bracket(phi, Pet, bm) + Pet * (Kappa(phi - Pe) + B0 * Grad_parP(jpar - Vpar) / B0); - if (smooth_separatrix) { - // Experimental smoothing across separatrix - ddt(Vort) += mesh->smoothSeparatrix(Vort); - } - if (filter_z) { ddt(Pe) = filter(ddt(Pe), 1); } diff --git a/examples/dalf3/data/BOUT.inp b/examples/dalf3/data/BOUT.inp index cd6070a1dd..53fa675f83 100644 --- a/examples/dalf3/data/BOUT.inp +++ b/examples/dalf3/data/BOUT.inp @@ -58,7 +58,7 @@ group_nonblock = false # Use non-blocking group operations? atol = 1e-08 # absolute tolerance rtol = 1e-05 # relative tolerance -use_precon = false # Use preconditioner: User-supplied or BBD +cvode_precon_method = none # Disable CVODE preconditioning use_jacobian = false # Use user-supplied Jacobian mxstep = 5000 # Number of internal steps between outputs diff --git a/examples/eigen-box/CMakeLists.txt b/examples/eigen-box/CMakeLists.txt index 76af4fbaa6..305e330652 100644 --- a/examples/eigen-box/CMakeLists.txt +++ b/examples/eigen-box/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(eigen-box LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(eigen-box +bout_add_example( + eigen-box SOURCES eigen-box.cxx - EXTRA_FILES eigenvals.py) + EXTRA_FILES eigenvals.py +) diff --git a/examples/eigen-box/eigenvals.py b/examples/eigen-box/eigenvals.py index 7b52fc60f2..5a71aabfc6 100755 --- a/examples/eigen-box/eigenvals.py +++ b/examples/eigen-box/eigenvals.py @@ -27,7 +27,8 @@ def plot_eigenvals(eigenvalues, eigenvectors=None): raise ValueError("Expecting eigenvectors to be 2D") if eigenvectors.shape[0] != len(eigenvalues): raise ValueError( - "First dimension of eigenvectors must match length of eigenvalues") + "First dimension of eigenvectors must match length of eigenvalues" + ) # If no eigenvectors supplied, only plot eigenvalues, otherwise # eigenvalues and eigenvectors @@ -44,18 +45,18 @@ def plot_eigenvals(eigenvalues, eigenvectors=None): range_r = amax(eigs_r) - amin(eigs_r) range_i = amax(eigs_i) - amin(eigs_i) - ax[0].plot(eigs_r, eigs_i, 'x') + ax[0].plot(eigs_r, eigs_i, "x") ax[0].set_xlabel("Real component") ax[0].set_ylabel("Imaginary component") ax[0].set_title("Eigenvalue") - overplot, = ax[0].plot([], [], 'ok') + (overplot,) = ax[0].plot([], [], "ok") if eigenvectors is not None: # Add a eigenvectors plot - vector_r, = ax[1].plot([], [], '-k', label="Real") - vector_i, = ax[1].plot([], [], '-r', label="Imag") - ax[1].legend(loc='upper right') + (vector_r,) = ax[1].plot([], [], "-k", label="Real") + (vector_i,) = ax[1].plot([], [], "-r", label="Imag") + ax[1].legend(loc="upper right") ax[1].set_xlabel("X") ax[1].set_ylabel("Amplitude") ax[1].set_title("Eigenvector") @@ -68,33 +69,33 @@ def onclick(event): # Find closest eigenvectors point, but stretch axes so # real and imaginary components are weighted equally - if(range_r == 0): - dist = ((eigs_i - event.ydata)/range_i)**2 - elif(range_i == 0): - dist = ((eigs_r - event.xdata)/range_r)**2 + if range_r == 0: + dist = ((eigs_i - event.ydata) / range_i) ** 2 + elif range_i == 0: + dist = ((eigs_r - event.xdata) / range_r) ** 2 else: - dist = ((eigs_r - event.xdata)/range_r)**2 + \ - ((eigs_i - event.ydata)/range_i)**2 + dist = ((eigs_r - event.xdata) / range_r) ** 2 + ( + (eigs_i - event.ydata) / range_i + ) ** 2 ind = argmin(dist) # Update the highlight plot overplot.set_data([eigs_r[ind]], [eigs_i[ind]]) - print("Eigenvalue number: %d (%e,%e)" % - (ind, eigs_r[ind], eigs_i[ind])) + print("Eigenvalue number: %d (%e,%e)" % (ind, eigs_r[ind], eigs_i[ind])) if eigenvectors is not None: # Update plots nx = eigenvectors.shape[1] - vector_r.set_data(arange(nx), eigenvectors[2*ind, :]) - vector_i.set_data(arange(nx), eigenvectors[2*ind+1, :]) + vector_r.set_data(arange(nx), eigenvectors[2 * ind, :]) + vector_i.set_data(arange(nx), eigenvectors[2 * ind + 1, :]) ax[1].relim() ax[1].autoscale_view() fig.canvas.draw() - fig.canvas.mpl_connect('button_press_event', onclick) + fig.canvas.mpl_connect("button_press_event", onclick) plt.show() diff --git a/examples/elm-pb-outerloop/CMakeLists.txt b/examples/elm-pb-outerloop/CMakeLists.txt index 008918a87f..12dc2f7696 100644 --- a/examples/elm-pb-outerloop/CMakeLists.txt +++ b/examples/elm-pb-outerloop/CMakeLists.txt @@ -2,16 +2,16 @@ cmake_minimum_required(VERSION 3.13) project(elm_pb LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(elm_pb_outerloop +bout_add_example( + elm_pb_outerloop SOURCES elm_pb_outerloop.cxx EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc ) if(BOUT_HAS_CUDA) - set_source_files_properties(elm_pb_outerloop.cxx PROPERTIES LANGUAGE CUDA ) + set_source_files_properties(elm_pb_outerloop.cxx PROPERTIES LANGUAGE CUDA) endif() - diff --git a/examples/elm-pb-outerloop/data/BOUT.inp b/examples/elm-pb-outerloop/data/BOUT.inp index 797ad91125..560da2999f 100644 --- a/examples/elm-pb-outerloop/data/BOUT.inp +++ b/examples/elm-pb-outerloop/data/BOUT.inp @@ -49,7 +49,7 @@ fft_measurement_flag = measure # If using FFTW, perform tests to determine fast atol = 1e-08 # absolute tolerance rtol = 1e-05 # relative tolerance -use_precon = false # Use preconditioner: User-supplied or BBD +cvode_precon_method = none # Disable CVODE preconditioning mxstep = 5000 # Number of internal steps between outputs output_step = 1 # time between outputs diff --git a/examples/elm-pb-outerloop/elm_pb_outerloop.cxx b/examples/elm-pb-outerloop/elm_pb_outerloop.cxx index 8e84901806..dbad416281 100644 --- a/examples/elm-pb-outerloop/elm_pb_outerloop.cxx +++ b/examples/elm-pb-outerloop/elm_pb_outerloop.cxx @@ -28,33 +28,26 @@ /*******************************************************************************/ +#include "bout/build_defines.hxx" +#include "bout/options.hxx" #include #include #include +#include #include #include #include #include #include -#include -#include -#include - -#include - -#include -#include #include +#include // Defines BOUT_FOR_RAJA #include #include +#include +#include +#include -#include // Defines BOUT_FOR_RAJA - -#if BOUT_HAS_HYPRE -#include -#endif - -#include +#include CELL_LOC loc = CELL_CENTRE; @@ -91,6 +84,10 @@ BOUT_OVERRIDE_DEFAULT_OPTION("phi:bndry_target", "neumann"); BOUT_OVERRIDE_DEFAULT_OPTION("phi:bndry_xin", "none"); BOUT_OVERRIDE_DEFAULT_OPTION("phi:bndry_xout", "none"); +#if BOUT_HAS_HYPRE +BOUT_OVERRIDE_DEFAULT_OPTION("laplacexy:type", "hypre"); +#endif + /// 3-field ELM simulation class ELMpb : public PhysicsModel { private: @@ -242,11 +239,7 @@ class ELMpb : public PhysicsModel { bool split_n0; // Solve the n=0 component of potential -#if BOUT_HAS_HYPRE - std::unique_ptr laplacexy{nullptr}; // Laplacian solver in X-Y (n=0) -#else std::unique_ptr laplacexy{nullptr}; // Laplacian solver in X-Y (n=0) -#endif Field2D phi2D; // Axisymmetric phi @@ -289,8 +282,7 @@ class ELMpb : public PhysicsModel { BoutReal damp_t_const; // Timescale of damping // Metric coefficients - Field2D Rxy, Bpxy, Btxy, B0, hthe; - Field2D I; // Shear factor + Field2D B0; const BoutReal MU0 = 4.0e-7 * PI; const BoutReal Mi = 2.0 * 1.6726e-27; // Ion mass @@ -369,24 +361,7 @@ class ELMpb : public PhysicsModel { // Load data from the grid // Load 2D profiles - mesh->get(J0, "Jpar0"); // A / m^2 - mesh->get(P0, "pressure"); // Pascals - - // Load curvature term - b0xcv.covariant = false; // Read contravariant components - mesh->get(b0xcv, "bxcv"); // mixed units x: T y: m^-2 z: m^-2 - - // Load metrics - if (mesh->get(Rxy, "Rxy") != 0) { // m - throw BoutException("Error: Cannot read Rxy from grid\n"); - } - if (mesh->get(Bpxy, "Bpxy") != 0) { // T - throw BoutException("Error: Cannot read Bpxy from grid\n"); - } - mesh->get(Btxy, "Btxy"); // T - mesh->get(B0, "Bxy"); // T - mesh->get(hthe, "hthe"); // m - mesh->get(I, "sinty"); // m^-2 T^-1 + mesh->get(P0, "pressure"); // Pascals mesh->get(Psixy, "psixy"); // get Psi mesh->get(Psiaxis, "psi_axis"); // axis flux mesh->get(Psibndry, "psi_bndry"); // edge flux @@ -567,13 +542,11 @@ class ELMpb : public PhysicsModel { split_n0 = options["split_n0"] .doc("Solve zonal (n=0) component of potential using LaplaceXY?") .withDefault(false); + if (split_n0) { // Create an XY solver for n=0 component -#if BOUT_HAS_HYPRE - laplacexy = bout::utils::make_unique(mesh); -#else - laplacexy = bout::utils::make_unique(mesh); -#endif + laplacexy = LaplaceXY::create(mesh); + // Set coefficients for Boussinesq solve laplacexy->setCoefs(1.0, 0.0); phi2D = 0.0; // Starting guess @@ -720,8 +693,6 @@ class ELMpb : public PhysicsModel { Dphi0 *= -1; } - V0 = -Rxy * Bpxy * Dphi0 / B0; - if (simple_rmp) { include_rmp = true; } @@ -805,46 +776,60 @@ class ELMpb : public PhysicsModel { } } - if (!include_curvature) { + ////////////////////////////////////////////////////////////// + // Read, normalise, and set coordinates + + // Typical magnetic field + mesh->get(Bbar, "bmag", 1.0); + // Typical length scale + mesh->get(Lbar, "rmag", 1.0); + + const auto tokamak_coords = + bout::set_tokamak_coordinates(*mesh, Lbar, Bbar, noshear or mesh->IncIntShear); + const auto& Rxy = tokamak_coords.Rxy; + const auto& Bpxy = tokamak_coords.Bpxy; + const auto& Btxy = tokamak_coords.Btxy; + const auto& hthe = tokamak_coords.hthe; + const auto& I = tokamak_coords.I_unnormalised; + + B0 = tokamak_coords.Bxy; + + // Need to unnormalise Rxy, as we normalise velocity separately + V0 = -(Rxy * Lbar) * Bpxy * Dphi0 / B0; + + if (include_curvature) { + // Load curvature term + b0xcv.covariant = false; // Read contravariant components + mesh->get(b0xcv, "bxcv"); // mixed units x: T y: m^-2 z: m^-2 + if (noshear) { + b0xcv.z += I * b0xcv.x; + } + } else { b0xcv = 0.0; } - if (!include_jpar0) { + if (include_jpar0) { + mesh->get(J0, "Jpar0"); // A / m^2 + } else { J0 = 0.0; } - if (noshear) { - if (include_curvature) { - b0xcv.z += I * b0xcv.x; - } - I = 0.0; - } - ////////////////////////////////////////////////////////////// // SHIFTED RADIAL COORDINATES if (mesh->IncIntShear) { // BOUT-06 style, using d/dx = d/dpsi + I * d/dz - metric->IntShiftTorsion = I; - + mesh->getCoordinates()->IntShiftTorsion = I; } else { // Dimits style, using local coordinate system if (include_curvature) { b0xcv.z += I * b0xcv.x; } - I = 0.0; // I disappears from metric } ////////////////////////////////////////////////////////////// // NORMALISE QUANTITIES - if (mesh->get(Bbar, "bmag") != 0) { // Typical magnetic field - Bbar = 1.0; - } - if (mesh->get(Lbar, "rmag") != 0) { // Typical length scale - Lbar = 1.0; - } - Va = sqrt(Bbar * Bbar / (MU0 * density * Mi)); Tbar = Lbar / Va; @@ -945,8 +930,8 @@ class ELMpb : public PhysicsModel { output.write(" drop K-H term\n"); } - Field2D Te; - Te = P0 / (2.0 * density * 1.602e-19); // Temperature in eV + // Temperature in eV + const Field2D Te = P0 / (2.0 * density * 1.602e-19); J0 = -MU0 * Lbar * J0 / B0; P0 = 2.0 * MU0 * P0 / (Bbar * Bbar); @@ -957,14 +942,6 @@ class ELMpb : public PhysicsModel { b0xcv.y *= Lbar * Lbar; b0xcv.z *= Lbar * Lbar; - Rxy /= Lbar; - Bpxy /= Bbar; - Btxy /= Bbar; - B0 /= Bbar; - hthe /= Lbar; - metric->dx /= Lbar * Lbar * Bbar; - I *= Lbar * Lbar * Bbar; - if (constn0) { T0_fake_prof = false; n0_fake_prof = false; @@ -1031,7 +1008,8 @@ class ELMpb : public PhysicsModel { vacuum_trans *= pnorm; // Transitions from 0 in core to 1 in vacuum - vac_mask = (1.0 - tanh((P0 - vacuum_pressure) / vacuum_trans)) / 2.0; + Field2D tanh_res = tanh((P0 - vacuum_pressure) / vacuum_trans); + vac_mask = (1.0 - tanh_res) / 2.0; if (spitzer_resist) { // Use Spitzer resistivity @@ -1090,27 +1068,6 @@ class ELMpb : public PhysicsModel { rmp_Psi = 0.0; } - /**************** CALCULATE METRICS ******************/ - - metric->g11 = SQ(Rxy * Bpxy); - metric->g22 = 1.0 / SQ(hthe); - metric->g33 = SQ(I) * metric->g11 + SQ(B0) / metric->g11; - metric->g12 = 0.0; - metric->g13 = -I * metric->g11; - metric->g23 = -Btxy / (hthe * Bpxy * Rxy); - - metric->J = hthe / Bpxy; - metric->Bxy = B0; - - metric->g_11 = 1.0 / metric->g11 + SQ(I * Rxy); - metric->g_22 = SQ(B0 * hthe / Bpxy); - metric->g_33 = Rxy * Rxy; - metric->g_12 = Btxy * hthe * I * Rxy / Bpxy; - metric->g_13 = I * Rxy * Rxy; - metric->g_23 = Btxy * hthe * Rxy / Bpxy; - - metric->geometry(); // Calculate quantities from metric tensor - // Set B field vector B0vec.covariant = false; @@ -1213,7 +1170,7 @@ class ELMpb : public PhysicsModel { // Only if not restarting: Check initial perturbation // Set U to zero where P0 < vacuum_pressure - U = where(P0 - vacuum_pressure, U, 0.0); + U = where(Field2D{P0 - vacuum_pressure}, U, 0.0); if (constn0) { ubyn = U; @@ -1680,12 +1637,12 @@ class ELMpb : public PhysicsModel { #endif }; - // Terms which are not yet single index operators - // Note: Terms which are included in the single index loop - // may be commented out here, to allow comparison/testing + // Terms which are not yet single index operators + // Note: Terms which are included in the single index loop + // may be commented out here, to allow comparison/testing - //////////////////////////////////////////////////// - // Parallel electric field + //////////////////////////////////////////////////// + // Parallel electric field #if not EVOLVE_JPAR // Vector potential @@ -1840,7 +1797,8 @@ class ELMpb : public PhysicsModel { ddt(U) -= 0.5 * Upara2 * bracket(Pi0, Dperp2Phi, bm_exb) / B0; Field3D B0phi = B0 * phi; mesh->communicate(B0phi); - Field3D B0phi0 = B0 * phi0; + Field2D res = B0 * phi0; + Field3D B0phi0 = res; mesh->communicate(B0phi0); ddt(U) += 0.5 * Upara2 * bracket(B0phi, Dperp2Pi0, bm_exb) / B0; ddt(U) += 0.5 * Upara2 * bracket(B0phi0, Dperp2Pi, bm_exb) / B0; diff --git a/examples/elm-pb/CMakeLists.txt b/examples/elm-pb/CMakeLists.txt index 8c672822cf..6cdd0f9cd9 100644 --- a/examples/elm-pb/CMakeLists.txt +++ b/examples/elm-pb/CMakeLists.txt @@ -2,14 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(elm_pb LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(elm_pb +bout_add_example( + elm_pb SOURCES elm_pb.cxx - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc - data/BOUT.inp - data-hypre/BOUT.inp - data-nonlinear/BOUT.inp) - + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc data/BOUT.inp data-hypre/BOUT.inp + data-nonlinear/BOUT.inp +) diff --git a/examples/elm-pb/Python/2dprofile.py b/examples/elm-pb/Python/2dprofile.py index 5bb01972f9..e95709ab5b 100644 --- a/examples/elm-pb/Python/2dprofile.py +++ b/examples/elm-pb/Python/2dprofile.py @@ -4,81 +4,92 @@ import numpy as np import matplotlib.pyplot as plt from boututils.datafile import DataFile -from matplotlib.ticker import FixedFormatter, FormatStrFormatter, AutoLocator, AutoMinorLocator +from matplotlib.ticker import ( + FixedFormatter, + FormatStrFormatter, + AutoLocator, + AutoMinorLocator, +) with DataFile("./cbm18_dens8.grid_nx68ny64.nc") as f: g = {v: f.read(v) for v in f.keys()} -majorLocator = AutoLocator() -majorFormatter = FormatStrFormatter('%3.0e') -minorLocator = AutoMinorLocator() -Fm = FixedFormatter(['0','$1 \\times 10^4$','$2 \\times 10^4$','$3 \\times 10^4$','$4 \\times 10^4$']) -Fm2 = FixedFormatter(['0','$2 \\times 10^5$','$4 \\times 10^5$','$6 \\times 10^5$']) - -bxy=g.get('Bxy') -p=g.get('pressure') -jpar0=g.get('Jpar0') -psixy=g.get('psixy') -btxy=g.get('Btxy') -shiftangle=g.get('ShiftAngle') - -nx=g.get('nx') -ny=g.get('ny') +majorLocator = AutoLocator() +majorFormatter = FormatStrFormatter("%3.0e") +minorLocator = AutoMinorLocator() +Fm = FixedFormatter( + [ + "0", + "$1 \\times 10^4$", + "$2 \\times 10^4$", + "$3 \\times 10^4$", + "$4 \\times 10^4$", + ] +) +Fm2 = FixedFormatter(["0", "$2 \\times 10^5$", "$4 \\times 10^5$", "$6 \\times 10^5$"]) + +bxy = g.get("Bxy") +p = g.get("pressure") +jpar0 = g.get("Jpar0") +psixy = g.get("psixy") +btxy = g.get("Btxy") +shiftangle = g.get("ShiftAngle") + +nx = g.get("nx") +ny = g.get("ny") q = np.zeros((nx, ny)) for i in range(ny): - q[:,i] = old_div(- shiftangle, (2 * np.pi)) - - + q[:, i] = old_div(-shiftangle, (2 * np.pi)) -xarr = psixy[:,0] +xarr = psixy[:, 0] xarr = old_div((xarr + 0.854856), (0.854856 + 0.0760856)) -fig=plt.figure() -plt.plot(xarr,q[:,32]) -plt.xlabel('normalized $\psi$', fontsize=25) -plt.ylabel('$q$',rotation='horizontal',fontsize=25) +fig = plt.figure() +plt.plot(xarr, q[:, 32]) +plt.xlabel("normalized $\psi$", fontsize=25) +plt.ylabel("$q$", rotation="horizontal", fontsize=25) fig.set_tight_layout(True) fig, ax1 = plt.subplots() -ax1.plot(xarr, p[:,32], 'r-', markevery=1, linewidth=3) -ax1.set_xlabel('normalized $\psi$',fontsize=25) +ax1.plot(xarr, p[:, 32], "r-", markevery=1, linewidth=3) +ax1.set_xlabel("normalized $\psi$", fontsize=25) # Make the y-axis label and tick labels match the line color. -ax1.set_ylabel('Pressure [Pa]', color='k',fontsize=25) +ax1.set_ylabel("Pressure [Pa]", color="k", fontsize=25) -#set y limit -ax1.set_ylim(0,40000,10000) +# set y limit +ax1.set_ylim(0, 40000, 10000) -#define ticks# +# define ticks# ax1.yaxis.set_ticks(np.arange(0, 40000, 10000)) -#ax1.yaxis.set_major_locator(majorLocator) -#ax1.yaxis.set_major_formatter(majorFormatter) +# ax1.yaxis.set_major_locator(majorLocator) +# ax1.yaxis.set_major_formatter(majorFormatter) ax1.yaxis.set_major_formatter(Fm) -#for the minor ticks, use no labels; default NullFormatter +# for the minor ticks, use no labels; default NullFormatter ax1.xaxis.set_minor_locator(AutoMinorLocator()) ax1.yaxis.set_minor_locator(AutoMinorLocator(10)) -#format tick labels +# format tick labels for tl in ax1.get_yticklabels(): - tl.set_color('k') + tl.set_color("k") ax2 = ax1.twinx() s2 = -jpar0 -ax2.plot(xarr, s2[:,32], 'r-',markevery=1,linewidth=3) -ax2.set_ylabel('$J_\parallel [A/m^2]$', color='k',fontsize=25) -ax2.set_ylim(0,600000) +ax2.plot(xarr, s2[:, 32], "r-", markevery=1, linewidth=3) +ax2.set_ylabel("$J_\parallel [A/m^2]$", color="k", fontsize=25) +ax2.set_ylim(0, 600000) ax2.yaxis.set_ticks(np.arange(0, 600000, 200000)) ax2.yaxis.set_major_formatter(Fm2) for tl in ax2.get_yticklabels(): - tl.set_color('k') + tl.set_color("k") fig.set_tight_layout(True) @@ -86,4 +97,4 @@ plt.show() -#plt.savefig('2d.png', transparent=True) +# plt.savefig('2d.png', transparent=True) diff --git a/examples/elm-pb/Python/analysis.py b/examples/elm-pb/Python/analysis.py index 4faf85f8ce..0e4a3acfdd 100644 --- a/examples/elm-pb/Python/analysis.py +++ b/examples/elm-pb/Python/analysis.py @@ -6,24 +6,24 @@ import pylab as plt from boutdata.collect import collect -path='./data/' -var=collect('P', path=path) +path = "./data/" +var = collect("P", path=path) -dcvar=np.mean(var, axis=3) -rmsvar=np.sqrt(np.mean(var**2,axis=3)-dcvar**2) +dcvar = np.mean(var, axis=3) +rmsvar = np.sqrt(np.mean(var**2, axis=3) - dcvar**2) plt.figure() -plt.plot(rmsvar[:,34,32]) +plt.plot(rmsvar[:, 34, 32]) plt.show(block=False) -fvar=np.fft.rfft(var,axis=3) +fvar = np.fft.rfft(var, axis=3) plt.figure() -plt.plot(abs(fvar[:,34,32,1:10])) +plt.plot(abs(fvar[:, 34, 32, 1:10])) plt.show(block=False) plt.figure() -plt.semilogy(abs(fvar[:,34,32,1:7])) +plt.semilogy(abs(fvar[:, 34, 32, 1:7])) plt.show(block=False) plt.show() diff --git a/examples/elm-pb/Python/data/BOUT.inp b/examples/elm-pb/Python/data/BOUT.inp index f0970f22bd..73020e34d4 100644 --- a/examples/elm-pb/Python/data/BOUT.inp +++ b/examples/elm-pb/Python/data/BOUT.inp @@ -67,7 +67,7 @@ upwind = W3 atol = 1e-08 # absolute tolerance rtol = 1e-05 # relative tolerance -use_precon = false # Use preconditioner: User-supplied or BBD +cvode_precon_method = none # Disable CVODE preconditioning use_jacobian = false # Use user-supplied Jacobian mxstep = 5000 # Number of internal steps between outputs diff --git a/examples/elm-pb/Python/data0/BOUT.inp b/examples/elm-pb/Python/data0/BOUT.inp index 918530981c..330ee8bc06 100644 --- a/examples/elm-pb/Python/data0/BOUT.inp +++ b/examples/elm-pb/Python/data0/BOUT.inp @@ -68,7 +68,7 @@ upwind = W3 atol = 1e-08 # absolute tolerance rtol = 1e-05 # relative tolerance -use_precon = false # Use preconditioner: User-supplied or BBD +cvode_precon_method = none # Disable CVODE preconditioning use_jacobian = false # Use user-supplied Jacobian mxstep = 5000 # Number of internal steps between outputs diff --git a/examples/elm-pb/Python/elm_size.py b/examples/elm-pb/Python/elm_size.py index 0d5c105870..6ad2ed6a0d 100644 --- a/examples/elm-pb/Python/elm_size.py +++ b/examples/elm-pb/Python/elm_size.py @@ -4,87 +4,126 @@ from past.utils import old_div import numpy as np -def elm_size(dcp,p0,uedge,xmin=None,xmax=None,yind=None,Bbar=None): - - lis=[dcp,p0,uedge] - if np.size(lis) != 3 : + +def elm_size(dcp, p0, uedge, xmin=None, xmax=None, yind=None, Bbar=None): + lis = [dcp, p0, uedge] + if np.size(lis) != 3: print("lack of parameters") return 0 - - if xmin == None : xmin=0 - if xmax == None : xmax=327 - if yind == None : yind=63 # choose the poloidal location for 1D size - if Bbar == None : Bbar=1.992782 # the normalized magnetic field + if xmin == None: + xmin = 0 + if xmax == None: + xmax = 327 + if yind == None: + yind = 63 # choose the poloidal location for 1D size + if Bbar == None: + Bbar = 1.992782 # the normalized magnetic field - mydcp=dcp - myp0=p0 - g=uedge + mydcp = dcp + myp0 = p0 + g = uedge PI = 3.1415926 - MU0 = 4.0e-7*PI + MU0 = 4.0e-7 * PI - s=np.shape(mydcp) + s = np.shape(mydcp) - if np.ndim(mydcp) != 3 : + if np.ndim(mydcp) != 3: print("dcp should be 3D(t,x,y)") - - - nt=s[0] - nx=s[1] - ny=s[2] - - Dtheta=g['dy'] #using correct poloidal angle - psixy=g['psixy'] - R=g['Rxy'] - Bp=g['Bpxy'] - hthe=g['hthe'] - - Dpsi=np.zeros((nx,ny)) - Dpsi[0,:]=psixy[1,:]-psixy[0,:] - Dpsi[nx-1,:]=psixy[nx-1,:]-psixy[nx-2,:] - for i in range(1,nx-2): - Dpsi[i,:]=old_div((psixy[i+1,:]-psixy[i-1,:]),2) - - - Ddcp1=np.zeros(nt) - Ddcp2=np.zeros(nt) - Ddcp3=np.zeros(nt) - Tp01=0. - Tp02=0. - Tp03=0. - - for t in range(nt) : - Ddcp3[t]=2.0*PI*np.sum(mydcp[t,xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/Bp[xmin:xmax,:]) - Ddcp2[t]=np.sum(mydcp[t,xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/(R[xmin:xmax,:]*Bp[xmin:xmax,:])) - Ddcp1[t]=np.sum(mydcp[t,xmin:xmax,yind]*Dpsi[xmin:xmax,yind]/(R[xmin:xmax,yind]*Bp[xmin:xmax,yind])) - - - Tp03=2.0*PI*np.sum(myp0[xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/Bp[xmin:xmax,:]) - Tp02=np.sum(myp0[xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/(R[xmin:xmax,:]*Bp[xmin:xmax,:])) - Tp01=np.sum(myp0[xmin:xmax,yind]*Dpsi[xmin:xmax,yind]/(R[xmin:xmax,yind]*Bp[xmin:xmax,yind])) - - s1=np.zeros(nt) - s2=np.zeros(nt) - s3=np.zeros(nt) - E_loss=np.zeros(nt) - - s1=old_div(-Ddcp1,Tp01) #1D elm size - s2=old_div(-Ddcp2,Tp02) #2D elm size - s3=old_div(-Ddcp3,Tp03) #3D elm size - - E_loss=-Ddcp3*(0.5*Bbar*Bbar/MU0) #energy loss, unit J - E_total=Tp03*(0.5*Bbar*Bbar/MU0) #total energy, unit J + + nt = s[0] + nx = s[1] + ny = s[2] + + Dtheta = g["dy"] # using correct poloidal angle + psixy = g["psixy"] + R = g["Rxy"] + Bp = g["Bpxy"] + hthe = g["hthe"] + + Dpsi = np.zeros((nx, ny)) + Dpsi[0, :] = psixy[1, :] - psixy[0, :] + Dpsi[nx - 1, :] = psixy[nx - 1, :] - psixy[nx - 2, :] + for i in range(1, nx - 2): + Dpsi[i, :] = old_div((psixy[i + 1, :] - psixy[i - 1, :]), 2) + + Ddcp1 = np.zeros(nt) + Ddcp2 = np.zeros(nt) + Ddcp3 = np.zeros(nt) + Tp01 = 0.0 + Tp02 = 0.0 + Tp03 = 0.0 + + for t in range(nt): + Ddcp3[t] = ( + 2.0 + * PI + * np.sum( + mydcp[t, xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / Bp[xmin:xmax, :] + ) + ) + Ddcp2[t] = np.sum( + mydcp[t, xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / (R[xmin:xmax, :] * Bp[xmin:xmax, :]) + ) + Ddcp1[t] = np.sum( + mydcp[t, xmin:xmax, yind] + * Dpsi[xmin:xmax, yind] + / (R[xmin:xmax, yind] * Bp[xmin:xmax, yind]) + ) + + Tp03 = ( + 2.0 + * PI + * np.sum( + myp0[xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / Bp[xmin:xmax, :] + ) + ) + Tp02 = np.sum( + myp0[xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / (R[xmin:xmax, :] * Bp[xmin:xmax, :]) + ) + Tp01 = np.sum( + myp0[xmin:xmax, yind] + * Dpsi[xmin:xmax, yind] + / (R[xmin:xmax, yind] * Bp[xmin:xmax, yind]) + ) + + s1 = np.zeros(nt) + s2 = np.zeros(nt) + s3 = np.zeros(nt) + E_loss = np.zeros(nt) + + s1 = old_div(-Ddcp1, Tp01) # 1D elm size + s2 = old_div(-Ddcp2, Tp02) # 2D elm size + s3 = old_div(-Ddcp3, Tp03) # 3D elm size + + E_loss = -Ddcp3 * (0.5 * Bbar * Bbar / MU0) # energy loss, unit J + E_total = Tp03 * (0.5 * Bbar * Bbar / MU0) # total energy, unit J class ELM: pass - elmsize=ELM() - elmsize.s1=s1 - elmsize.s2=s2 - elmsize.s3=s3 - elmsize.E_loss=E_loss - elmsize.E_total=E_total + + elmsize = ELM() + elmsize.s1 = s1 + elmsize.s2 = s2 + elmsize.s3 = s3 + elmsize.E_loss = E_loss + elmsize.E_total = E_total return elmsize - - diff --git a/examples/elm-pb/Python/fftall.py b/examples/elm-pb/Python/fftall.py index e0a4385764..c6d5b87ded 100644 --- a/examples/elm-pb/Python/fftall.py +++ b/examples/elm-pb/Python/fftall.py @@ -2,8 +2,7 @@ from numpy import * from scipy.io import readsav -print('Calculating P..') -a=transpose(readsav('phi.idl.dat')['phi']) -fa=fft.fft(a,axis=2) -save('fp',fa) - +print("Calculating P..") +a = transpose(readsav("phi.idl.dat")["phi"]) +fa = fft.fft(a, axis=2) +save("fp", fa) diff --git a/examples/elm-pb/Python/fftall2.py b/examples/elm-pb/Python/fftall2.py index d864f356d0..64b08c437a 100644 --- a/examples/elm-pb/Python/fftall2.py +++ b/examples/elm-pb/Python/fftall2.py @@ -2,9 +2,9 @@ from numpy import * from boutdata.collect import collect -path='./data/' -data=collect('P',path=path) +path = "./data/" +data = collect("P", path=path) -print('Saving P..') -fa=fft.fft(data,axis=3) -save('fp',rollaxis(fa,0,4)) +print("Saving P..") +fa = fft.fft(data, axis=3) +save("fp", rollaxis(fa, 0, 4)) diff --git a/examples/elm-pb/Python/grate.py b/examples/elm-pb/Python/grate.py index 003f38ff8e..65d6da6449 100644 --- a/examples/elm-pb/Python/grate.py +++ b/examples/elm-pb/Python/grate.py @@ -8,12 +8,11 @@ from boututils.moment_xyzt import moment_xyzt +path = "./data/" -path='./data/' +p = collect("P", path=path) +rmsp_f = moment_xyzt(p[:, 34:35, 32:33, :], "RMS").rms -p=collect('P',path=path) -rmsp_f=moment_xyzt(p[:,34:35,32:33,:], 'RMS').rms - -print(np.gradient(np.log(rmsp_f[:,0,0]))[-1]) +print(np.gradient(np.log(rmsp_f[:, 0, 0]))[-1]) diff --git a/examples/elm-pb/Python/grate2.py b/examples/elm-pb/Python/grate2.py index f8172bcf08..af0768803a 100644 --- a/examples/elm-pb/Python/grate2.py +++ b/examples/elm-pb/Python/grate2.py @@ -2,6 +2,7 @@ from __future__ import division from builtins import range from past.utils import old_div + ### # computes average growth rate for all points at the final timestep # computes average growth rate for points in the mead plane at the final timestep @@ -10,40 +11,44 @@ from boutdata.collect import collect from boututils.moment_xyzt import moment_xyzt -path='./data/' +path = "./data/" + +p = collect("P", path=path) -p=collect('P',path=path) +nmpy = old_div(p.shape[2], 2) # define mead plane -nmpy=old_div(p.shape[2],2) # define mead plane +ik = 50 # disregard the first ik timesteps -ik = 50 # disregard the first ik timesteps def gr(p): - rmsp_f=moment_xyzt(p, 'RMS').rms + rmsp_f = moment_xyzt(p, "RMS").rms - ni=np.shape(rmsp_f)[1] - nj=np.shape(rmsp_f)[2] + ni = np.shape(rmsp_f)[1] + nj = np.shape(rmsp_f)[2] - growth=np.zeros((ni,nj)) + growth = np.zeros((ni, nj)) - for i in range(ni): - for j in range(nj): - growth[i,j]=np.gradient(np.log(rmsp_f[ik::,i,j]))[-1] + for i in range(ni): + for j in range(nj): + growth[i, j] = np.gradient(np.log(rmsp_f[ik::, i, j]))[-1] - return growth + return growth -growth=gr(p) +growth = gr(p) -d=np.ma.masked_array(growth,np.isnan(growth)) +d = np.ma.masked_array(growth, np.isnan(growth)) # masked arrays # http://stackoverflow.com/questions/5480694/numpy-calculate-averages-with-nans-removed -print('Total mean value = ', np.mean(np.ma.masked_array(d,np.isinf(d)))) -mm=np.ma.masked_array(growth[:,nmpy],np.isnan(growth[:,nmpy])) -if np.isinf(np.mean(mm)) : - print('There is an Inf value in the mead plane') - print('Mean value of floating numbers in mead plane is = ', np.mean(np.ma.masked_array(mm,np.isinf(mm)))) +print("Total mean value = ", np.mean(np.ma.masked_array(d, np.isinf(d)))) +mm = np.ma.masked_array(growth[:, nmpy], np.isnan(growth[:, nmpy])) +if np.isinf(np.mean(mm)): + print("There is an Inf value in the mead plane") + print( + "Mean value of floating numbers in mead plane is = ", + np.mean(np.ma.masked_array(mm, np.isinf(mm))), + ) else: - print('Mean value in mead plane= ', np.mean(mm)) + print("Mean value in mead plane= ", np.mean(mm)) diff --git a/examples/elm-pb/Python/plotcollapse.py b/examples/elm-pb/Python/plotcollapse.py index ee64ca381f..f68e7b06aa 100755 --- a/examples/elm-pb/Python/plotcollapse.py +++ b/examples/elm-pb/Python/plotcollapse.py @@ -10,49 +10,52 @@ import os from pathlib import Path -#Dynamic matplotlib settings +# Dynamic matplotlib settings from matplotlib import rcParams -rcParams['font.size'] = 20. -rcParams['legend.fontsize'] = 'small' -rcParams['lines.linewidth'] = 2 -if not os.path.exists('image'): - os.makedirs('image') +rcParams["font.size"] = 20.0 +rcParams["legend.fontsize"] = "small" +rcParams["lines.linewidth"] = 2 + +if not os.path.exists("image"): + os.makedirs("image") filename = Path(__file__).with_name("cbm18_dens8.grid_nx68ny64.nc") with DataFile(str(filename)) as f: g = {v: f.read(v) for v in f.keys()} -psi = old_div((g['psixy'][:, 32] - g['psi_axis']), (g['psi_bndry'] - g['psi_axis'])) +psi = old_div((g["psixy"][:, 32] - g["psi_axis"]), (g["psi_bndry"] - g["psi_axis"])) -path = './data' +path = "./data" plt.figure() -p0=collect('P0', path=path) +p0 = collect("P0", path=path) -p=collect('P', path=path) -res = moment_xyzt(p,'RMS','DC') +p = collect("P", path=path) +res = moment_xyzt(p, "RMS", "DC") rmsp = res.rms dcp = res.dc nt = dcp.shape[0] -plt.plot(psi, p0[:, 32], 'k--', label='t=0') -plt.plot(psi, p0[:, 32] + dcp[nt//4, :, 32], 'r-', label='t='+np.str(nt//4)) -plt.plot(psi, p0[:, 32] + dcp[nt//2, :, 32], 'g-', label='t='+np.str(nt//2)) -plt.plot(psi, p0[:, 32] + dcp[3*nt//4, :, 32], 'b-', label='t='+np.str(3*nt//4)) -plt.plot(psi, p0[:, 32] + dcp[-1, :, 32], 'c-', label='t='+np.str(nt)) +plt.plot(psi, p0[:, 32], "k--", label="t=0") +plt.plot(psi, p0[:, 32] + dcp[nt // 4, :, 32], "r-", label="t=" + np.str(nt // 4)) +plt.plot(psi, p0[:, 32] + dcp[nt // 2, :, 32], "g-", label="t=" + np.str(nt // 2)) +plt.plot( + psi, p0[:, 32] + dcp[3 * nt // 4, :, 32], "b-", label="t=" + np.str(3 * nt // 4) +) +plt.plot(psi, p0[:, 32] + dcp[-1, :, 32], "c-", label="t=" + np.str(nt)) plt.legend() -#plt.xlim(0.6, 1.0) -plt.xlabel(r'Normalized poloidal flux ($\psi$)') -plt.ylabel(r'$\langle p\rangle_\xi$') -plt.title(r'Pressure') +# plt.xlim(0.6, 1.0) +plt.xlabel(r"Normalized poloidal flux ($\psi$)") +plt.ylabel(r"$\langle p\rangle_\xi$") +plt.title(r"Pressure") xmin, xmax = plt.xlim() ymin, ymax = plt.ylim() -#plt.savefig('image/plotcollapse.png', bbox_inches='tight') -#plt.savefig('image/plotcollapse.eps', bbox_inches='tight') +# plt.savefig('image/plotcollapse.png', bbox_inches='tight') +# plt.savefig('image/plotcollapse.eps', bbox_inches='tight') plt.tight_layout() diff --git a/examples/elm-pb/Python/plotmode.py b/examples/elm-pb/Python/plotmode.py index d89fdaf940..9325ee8764 100644 --- a/examples/elm-pb/Python/plotmode.py +++ b/examples/elm-pb/Python/plotmode.py @@ -4,60 +4,57 @@ from builtins import range from past.utils import old_div -from numpy import *; -#from scipy.io import readsav; -import matplotlib.pyplot as plt; +from numpy import * + +# from scipy.io import readsav; +import matplotlib.pyplot as plt # Dynamic matplotlib settings -from matplotlib import rcParams; -rcParams['font.size'] = 20; -rcParams['legend.fontsize'] = 'small'; -rcParams['legend.labelspacing'] = 0.1; -rcParams['lines.linewidth'] = 2; -rcParams['savefig.bbox'] = 'tight'; +from matplotlib import rcParams +rcParams["font.size"] = 20 +rcParams["legend.fontsize"] = "small" +rcParams["legend.labelspacing"] = 0.1 +rcParams["lines.linewidth"] = 2 +rcParams["savefig.bbox"] = "tight" # Create image directory if not exists -import os; -if not os.path.exists('image'): - os.makedirs('image'); - -#fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; -fphi = load('fp.npy') - -plt.figure(); -for i in range(1, 9): - print("Growth rate for mode number", i) - print(gradient(log(abs(fphi[34, 32, i, :])))) - plt.semilogy(((abs(fphi[34, 32, i, :]))), label = 'n=' + str(i * 5)); +import os -plt.legend(loc=2); -plt.xlabel('Time'); -plt.savefig('image/plotmode.png'); -plt.savefig('image/plotmode.eps'); +if not os.path.exists("image"): + os.makedirs("image") +# fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; +fphi = load("fp.npy") -plt.show(block=False); -plt.figure(); +plt.figure() for i in range(1, 9): - plt.plot(abs(fphi[:, 32, i, -1]), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodeamp.png'); -plt.savefig('image/plotmodeamp.eps'); - -plt.show(block=False); - -plt.figure(); + print("Growth rate for mode number", i) + print(gradient(log(abs(fphi[34, 32, i, :])))) + plt.semilogy((abs(fphi[34, 32, i, :])), label="n=" + str(i * 5)) + +plt.legend(loc=2) +plt.xlabel("Time") +plt.savefig("image/plotmode.png") +plt.savefig("image/plotmode.eps") +plt.show(block=False) +plt.figure() for i in range(1, 9): - plt.plot(old_div(abs(fphi[:, 32, i, -1]),abs(fphi[:, 32, i, -1]).max()), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodenorm.png'); -plt.savefig('image/plotmodenorm.eps'); - -plt.show(); - + plt.plot(abs(fphi[:, 32, i, -1]), label="n=" + str(i * 5)) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodeamp.png") +plt.savefig("image/plotmodeamp.eps") +plt.show(block=False) +plt.figure() +for i in range(1, 9): + plt.plot( + old_div(abs(fphi[:, 32, i, -1]), abs(fphi[:, 32, i, -1]).max()), + label="n=" + str(i * 5), + ) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodenorm.png") +plt.savefig("image/plotmodenorm.eps") +plt.show() diff --git a/examples/elm-pb/Python/plotmode2.py b/examples/elm-pb/Python/plotmode2.py index d0c63f32d9..c298a3a5ef 100644 --- a/examples/elm-pb/Python/plotmode2.py +++ b/examples/elm-pb/Python/plotmode2.py @@ -4,63 +4,61 @@ from builtins import range from past.utils import old_div -from numpy import *; -#from scipy.io import readsav; -import matplotlib.pyplot as plt; +from numpy import * + +# from scipy.io import readsav; +import matplotlib.pyplot as plt from boutdata.collect import collect # Dynamic matplotlib settings -from matplotlib import rcParams; -rcParams['font.size'] = 20; -rcParams['legend.fontsize'] = 'small'; -rcParams['legend.labelspacing'] = 0.1; -rcParams['lines.linewidth'] = 2; -rcParams['savefig.bbox'] = 'tight'; +from matplotlib import rcParams +rcParams["font.size"] = 20 +rcParams["legend.fontsize"] = "small" +rcParams["legend.labelspacing"] = 0.1 +rcParams["lines.linewidth"] = 2 +rcParams["savefig.bbox"] = "tight" # Create image directory if not exists -import os; -if not os.path.exists('image'): - os.makedirs('image'); +import os + +if not os.path.exists("image"): + os.makedirs("image") -path='./data/' -data=collect('P',path=path) +path = "./data/" +data = collect("P", path=path) -#fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; +# fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; fphi = fft.fft(data, axis=3) -plt.figure(); +plt.figure() for i in range(1, 9): - print("Growth rate for mode number", i) - print(gradient(log(abs(fphi[:,34, 32, i])))) - plt.semilogy(((abs(fphi[:,34, 32, i]))), label = 'n=' + str(i * 5)); - -plt.legend(loc=2); -plt.xlabel('Time'); -plt.savefig('image/plotmode.png'); -plt.savefig('image/plotmode.eps'); - - -plt.show(block=False); -plt.figure(); + print("Growth rate for mode number", i) + print(gradient(log(abs(fphi[:, 34, 32, i])))) + plt.semilogy((abs(fphi[:, 34, 32, i])), label="n=" + str(i * 5)) + +plt.legend(loc=2) +plt.xlabel("Time") +plt.savefig("image/plotmode.png") +plt.savefig("image/plotmode.eps") +plt.show(block=False) +plt.figure() for i in range(1, 9): - plt.plot(abs(fphi[-1, :, 32, i]), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodeamp.png'); -plt.savefig('image/plotmodeamp.eps'); - -plt.show(block=False); - -plt.figure(); + plt.plot(abs(fphi[-1, :, 32, i]), label="n=" + str(i * 5)) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodeamp.png") +plt.savefig("image/plotmodeamp.eps") +plt.show(block=False) +plt.figure() for i in range(1, 9): - plt.plot(old_div(abs(fphi[-1, :, 32, i]),abs(fphi[-1, :, 32, i]).max()), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodenorm.png'); -plt.savefig('image/plotmodenorm.eps'); - -plt.show(); + plt.plot( + old_div(abs(fphi[-1, :, 32, i]), abs(fphi[-1, :, 32, i]).max()), + label="n=" + str(i * 5), + ) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodenorm.png") +plt.savefig("image/plotmodenorm.eps") +plt.show() diff --git a/examples/elm-pb/Python/plotphase.py b/examples/elm-pb/Python/plotphase.py index 9225e498ae..10f4279cf4 100755 --- a/examples/elm-pb/Python/plotphase.py +++ b/examples/elm-pb/Python/plotphase.py @@ -4,34 +4,33 @@ from numpy import save, load, angle import matplotlib.pyplot as plt -fphi = load('fphi.npy') +fphi = load("fphi.npy") -fte = load('fte.npy') -phase_te = angle(old_div(fphi,fte)) -save('phase_te', phase_te) +fte = load("fte.npy") +phase_te = angle(old_div(fphi, fte)) +save("phase_te", phase_te) plt.figure() -plt.plot(mean(mean(phase_te[:,:,3,:],axis=1),axis=1)) -plt.title('Te') -plt.savefig('image/phase_te.png') -plt.savefig('image/phase_te.eps') +plt.plot(mean(mean(phase_te[:, :, 3, :], axis=1), axis=1)) +plt.title("Te") +plt.savefig("image/phase_te.png") +plt.savefig("image/phase_te.eps") -fti = load('fti.npy') -phase_ti = angle(old_div(fphi,fti)) -save('phase_ti', phase_ti) +fti = load("fti.npy") +phase_ti = angle(old_div(fphi, fti)) +save("phase_ti", phase_ti) plt.figure() -plt.plot(mean(mean(phase_ti[:,:,3,:],axis=1),axis=1)) -plt.title('ti') -plt.savefig('image/phase_ti.png') -plt.savefig('image/phase_ti.eps') +plt.plot(mean(mean(phase_ti[:, :, 3, :], axis=1), axis=1)) +plt.title("ti") +plt.savefig("image/phase_ti.png") +plt.savefig("image/phase_ti.eps") -fni = load('fni.npy') -phase_ni = angle(old_div(fphi,fni)) -save('phase_ni', phase_ni) +fni = load("fni.npy") +phase_ni = angle(old_div(fphi, fni)) +save("phase_ni", phase_ni) plt.figure() -plt.plot(mean(mean(phase_ni[:,:,3,:],axis=1),axis=1)) -plt.title('ni') -plt.savefig('image/phase_ni.png') -plt.savefig('image/phase_ni.eps') +plt.plot(mean(mean(phase_ni[:, :, 3, :], axis=1), axis=1)) +plt.title("ni") +plt.savefig("image/phase_ni.png") +plt.savefig("image/phase_ni.eps") plt.show() - diff --git a/examples/elm-pb/Python/polslice.py b/examples/elm-pb/Python/polslice.py index fe78495b5a..6a1179b2ae 100644 --- a/examples/elm-pb/Python/polslice.py +++ b/examples/elm-pb/Python/polslice.py @@ -11,31 +11,33 @@ # Specify parameters -path='./data/' +path = "./data/" -variable="P" +variable = "P" p = collect(variable, path=path) -period=15 +period = 15 -grid='../cbm18_dens8.grid_nx68ny64.nc' +grid = "../cbm18_dens8.grid_nx68ny64.nc" ######################################################## # Call plotpolslice once to get extended poloidal grid -r,z,fun=plotpolslice(p[0,:,:,:],grid,period=period,rz=1) +r, z, fun = plotpolslice(p[0, :, :, :], grid, period=period, rz=1) -nx=r.shape[0] # number of points in r -ny=r.shape[1] # number of points in z -nt=p.shape[0] # time intervals +nx = r.shape[0] # number of points in r +ny = r.shape[1] # number of points in z +nt = p.shape[0] # time intervals -fm=np.zeros((nt,nx,ny)) # array to store the time sequence of the poloidal cross section +fm = np.zeros( + (nt, nx, ny) +) # array to store the time sequence of the poloidal cross section -#Compute all time frames +# Compute all time frames for k in range(nt): - fm[k,:,:]=plotpolslice(p[k,:,:,:],grid,period=period,rz=0) + fm[k, :, :] = plotpolslice(p[k, :, :, :], grid, period=period, rz=0) -np.savez('pslice',fm=fm, z=z, r=r) +np.savez("pslice", fm=fm, z=z, r=r) diff --git a/examples/elm-pb/Python/post.py b/examples/elm-pb/Python/post.py index 9bb67347f0..cdaefa98cf 100644 --- a/examples/elm-pb/Python/post.py +++ b/examples/elm-pb/Python/post.py @@ -15,12 +15,12 @@ from mayavi import mlab -path0="./data0/" -path1="./data/" +path0 = "./data0/" +path1 = "./data/" -period=15 +period = 15 -gfile='./cbm18_dens8.grid_nx68ny64.nc' +gfile = "./cbm18_dens8.grid_nx68ny64.nc" with DataFile(gfile) as f: @@ -28,71 +28,115 @@ Dphi0 = collect("Dphi0", path=path0) -phi0 = collect("phi0", path=path1) # needs diamagnetic effects +phi0 = collect("phi0", path=path1) # needs diamagnetic effects # -psixy=g.get('psixy') -PSI_AXIS=g.get('psi_axis') -PSI_BNDRY=g.get('psi_bndry') +psixy = g.get("psixy") +PSI_AXIS = g.get("psi_axis") +PSI_BNDRY = g.get("psi_bndry") # -psix=old_div((psixy[:,32]-PSI_AXIS),(PSI_BNDRY-PSI_AXIS)) -Epsi=-deriv(phi0[:,32],psix) +psix = old_div((psixy[:, 32] - PSI_AXIS), (PSI_BNDRY - PSI_AXIS)) +Epsi = -deriv(phi0[:, 32], psix) # # -fig=figure() -plot(psix,-Dphi0[:,32], 'r', linewidth=5) -plot(psix,Epsi,'k',linewidth=5) -annotate('w/o flow', xy=(.3, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', size=30) -annotate('w/ flow', xy=(.7, .4), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', color='r', size=30) -xlabel('Radial $\psi$',fontsize=25) -ylabel('$\Omega(\psi)/\omega_A$',fontsize=25) -ylim([-.05,0]) -xlim([0.4,1.2]) +fig = figure() +plot(psix, -Dphi0[:, 32], "r", linewidth=5) +plot(psix, Epsi, "k", linewidth=5) +annotate( + "w/o flow", + xy=(0.3, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + size=30, +) +annotate( + "w/ flow", + xy=(0.7, 0.4), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + color="r", + size=30, +) +xlabel("Radial $\psi$", fontsize=25) +ylabel("$\Omega(\psi)/\omega_A$", fontsize=25) +ylim([-0.05, 0]) +xlim([0.4, 1.2]) fig.set_tight_layout(True) show(block=False) p_f0 = collect("P", path=path0) p_f = collect("P", path=path1) # -rmsp_f0=moment_xyzt(p_f0, 'RMS').rms -rmsp_f=moment_xyzt(p_f, 'RMS').rms +rmsp_f0 = moment_xyzt(p_f0, "RMS").rms +rmsp_f = moment_xyzt(p_f, "RMS").rms # -fig=figure(figsize=(10, 8)) -plot(np.gradient(np.log(rmsp_f0[:,34,32])), color='k',linewidth=3) -plot(np.gradient(np.log(rmsp_f[:,34,32])),color='red',linewidth=3) - -ylabel('$\gamma / \omega_A$',fontsize=25) -xlabel('Time$(\\tau_A)$',fontsize=25) -annotate('w/o flow', xy=(.5, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', size=30) -annotate('w/ flow', xy=(.5, .4), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', color='r', size=30) -ylim([0,0.5]) -xlim([0,100]) +fig = figure(figsize=(10, 8)) +plot(np.gradient(np.log(rmsp_f0[:, 34, 32])), color="k", linewidth=3) +plot(np.gradient(np.log(rmsp_f[:, 34, 32])), color="red", linewidth=3) + +ylabel("$\gamma / \omega_A$", fontsize=25) +xlabel("Time$(\\tau_A)$", fontsize=25) +annotate( + "w/o flow", + xy=(0.5, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + size=30, +) +annotate( + "w/ flow", + xy=(0.5, 0.4), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + color="r", + size=30, +) +ylim([0, 0.5]) +xlim([0, 100]) fig.set_tight_layout(True) show(block=False) - -plotpolslice(p_f0[50,:,:,:],gfile,period=period, fig=1) -mlab.text(.01,.99,"w/o flow") - -plotpolslice(p_f[50,:,:,:],gfile,period=period, fig=1) -mlab.text(.01,.99,"w/ flow") - -fig=figure() -mode_structure(p_f0[50,:,:,:], g, period=period) -plot([40,40],[0,.014],'k--',linewidth=5) -annotate('w/o flow', xy=(.3, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', size=30) -ylim([0,0.014]) -xlim([0,80]) +plotpolslice(p_f0[50, :, :, :], gfile, period=period, fig=1) +mlab.text(0.01, 0.99, "w/o flow") + +plotpolslice(p_f[50, :, :, :], gfile, period=period, fig=1) +mlab.text(0.01, 0.99, "w/ flow") + +fig = figure() +mode_structure(p_f0[50, :, :, :], g, period=period) +plot([40, 40], [0, 0.014], "k--", linewidth=5) +annotate( + "w/o flow", + xy=(0.3, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + size=30, +) +ylim([0, 0.014]) +xlim([0, 80]) fig.set_tight_layout(True) show(block=False) figure() -mode_structure(p_f[50,:,:,:], g, period=period) -plot([40,40],[0,.014],'k--',linewidth=5) -annotate('w/ flow', xy=(.3, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', color='k', size=30) -ylim([0,0.0001]) -xlim([0,80]) +mode_structure(p_f[50, :, :, :], g, period=period) +plot([40, 40], [0, 0.014], "k--", linewidth=5) +annotate( + "w/ flow", + xy=(0.3, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + color="k", + size=30, +) +ylim([0, 0.0001]) +xlim([0, 80]) show(block=False) show() diff --git a/examples/elm-pb/Python/read_elmsize.py b/examples/elm-pb/Python/read_elmsize.py index fcd3d24e53..340f57ce7b 100644 --- a/examples/elm-pb/Python/read_elmsize.py +++ b/examples/elm-pb/Python/read_elmsize.py @@ -4,12 +4,12 @@ from pylab import save, figure, plot, title, xlabel, ylabel, show, tight_layout from elm_size import elm_size -path='./data' +path = "./data" -t_array=collect('t_array', path=path) -save('t_array.dat', t_array) -p0=collect('P0', path=path) -save('p0.dat', p0) +t_array = collect("t_array", path=path) +save("t_array.dat", t_array) +p0 = collect("P0", path=path) +save("p0.dat", p0) # n0=collect('n0', path=path) @@ -22,27 +22,27 @@ with DataFile("./cbm18_dens8.grid_nx68ny64.nc") as f: gfile = {v: f.read(v) for v in f.keys()} -p=collect('P', path=path) -save('p.dat', p) -res=moment_xyzt(p,'RMS','DC') -rmsp=res.rms -dcp=res.dc -save('rmsp.dat', rmsp) -save('dcp.dat', dcp) -elmsp=elm_size(dcp,p0,gfile,yind=32,Bbar=gfile['bmag']) -save('elmsp.dat', elmsp) +p = collect("P", path=path) +save("p.dat", p) +res = moment_xyzt(p, "RMS", "DC") +rmsp = res.rms +dcp = res.dc +save("rmsp.dat", rmsp) +save("dcp.dat", dcp) +elmsp = elm_size(dcp, p0, gfile, yind=32, Bbar=gfile["bmag"]) +save("elmsp.dat", elmsp) figure(0) -plot(t_array,elmsp.s2, 'k-') -xlabel('t/Ta') -ylabel('Elm size') -title('Elm size, P') +plot(t_array, elmsp.s2, "k-") +xlabel("t/Ta") +ylabel("Elm size") +title("Elm size, P") tight_layout() show() -phi=collect('phi', path=path ) -save('phi.dat', phi) -res=moment_xyzt( phi, 'DC', 'RMS') -save('dcphi.dat',res.dc) -save('rmsphi.dat', res.rms) +phi = collect("phi", path=path) +save("phi.dat", phi) +res = moment_xyzt(phi, "DC", "RMS") +save("dcphi.dat", res.dc) +save("rmsphi.dat", res.rms) diff --git a/examples/elm-pb/Python/showpolslice.py b/examples/elm-pb/Python/showpolslice.py index d292575e35..072c53dcd6 100644 --- a/examples/elm-pb/Python/showpolslice.py +++ b/examples/elm-pb/Python/showpolslice.py @@ -6,10 +6,12 @@ import numpy as np from tvtk.tools import visual + try: from enthought.mayavi import mlab except ImportError: - try: from mayavi import mlab + try: + from mayavi import mlab except ImportError: print("No mlab available") @@ -17,40 +19,42 @@ ########################### # Read polslice array -npzfile=np.load('pslice.npz') -r=npzfile['r'] -z=npzfile['z'] -fm=npzfile['fm'] +npzfile = np.load("pslice.npz") +r = npzfile["r"] +z = npzfile["z"] +fm = npzfile["fm"] ######################################################## # Set up the window -f = mlab.figure(size=(800,600)) +f = mlab.figure(size=(800, 600)) # Tell visual to use this as the viewer. visual.set_viewer(f) ######################################################## # Do the appropriate graph -#s = mlab.contour_surf(r,z,fun, contours=30, line_width=.5, transparent=True) -#s=mlab.surf(r,z,fun, colormap='Spectral') -s = mlab.mesh(r,z,fm[0,:,:], scalars=fm[0,:,:], colormap='PuOr')#, wrap_scale='true')#, representation='wireframe') -s.enable_contours=True -s.contour.filled_contours=True +# s = mlab.contour_surf(r,z,fun, contours=30, line_width=.5, transparent=True) +# s=mlab.surf(r,z,fun, colormap='Spectral') +s = mlab.mesh( + r, z, fm[0, :, :], scalars=fm[0, :, :], colormap="PuOr" +) # , wrap_scale='true')#, representation='wireframe') +s.enable_contours = True +s.contour.filled_contours = True # Define perspective and optional attributes. You can also implement from the window afterwards -mlab.view(0,0) -#mlab.view(-94.159958841373324, +mlab.view(0, 0) +# mlab.view(-94.159958841373324, # 53.777002382688906, # 8.2311808018087582) mlab.draw(f) mlab.colorbar(orientation="vertical") -#mlab.axes() -#mlab.outline() +# mlab.axes() +# mlab.outline() ######################################################## # mlab animation -anim(s,fm, save=True) +anim(s, fm, save=True) diff --git a/examples/elm-pb/Python/sprofiles.py b/examples/elm-pb/Python/sprofiles.py index 9d8eea48de..244599f8af 100644 --- a/examples/elm-pb/Python/sprofiles.py +++ b/examples/elm-pb/Python/sprofiles.py @@ -16,45 +16,46 @@ with DataFile(gfile) as f: g = {v: f.read(v) for v in f.keys()} -var=collect("P", path=path) +var = collect("P", path=path) -sol=surface_average(var, g) -#sol=np.mean(var,axis=3) +sol = surface_average(var, g) +# sol=np.mean(var,axis=3) -p0av=collect("P0", path=path) +p0av = collect("P0", path=path) -q=np.zeros(sol.shape) +q = np.zeros(sol.shape) for i in range(sol.shape[1]): - q[:,i]=sol[:,i]+p0av[:,0] + q[:, i] = sol[:, i] + p0av[:, 0] -psixy=g.get('psixy') -psi0=g.get('psi_axis') -psix=g.get('psi_bndry') +psixy = g.get("psixy") +psi0 = g.get("psi_axis") +psix = g.get("psi_bndry") -xarr = psixy[:,0] -xarr = old_div((xarr - psi0), (-psi0 + psix)) #for this grid +xarr = psixy[:, 0] +xarr = old_div((xarr - psi0), (-psi0 + psix)) # for this grid -fig=figure() +fig = figure() -nt=q.shape[1] +nt = q.shape[1] -plot(xarr, p0av,'k',label='t=0') -plot(xarr,q[:,nt/4],'r',label='t='+np.str(nt/4)) -plot(xarr,q[:,nt/2],'b',label='t='+np.str(nt/2)) -plot(xarr,q[:,3*nt/4],'g',label='t='+np.str(3*nt/4)) -plot(xarr, q[:,-1],'k',label='t='+np.str(nt)) +plot(xarr, p0av, "k", label="t=0") +plot(xarr, q[:, nt / 4], "r", label="t=" + np.str(nt / 4)) +plot(xarr, q[:, nt / 2], "b", label="t=" + np.str(nt / 2)) +plot(xarr, q[:, 3 * nt / 4], "g", label="t=" + np.str(3 * nt / 4)) +plot(xarr, q[:, -1], "k", label="t=" + np.str(nt)) from collections import OrderedDict + handles, labels = gca().get_legend_handles_labels() by_label = OrderedDict(list(zip(labels, handles))) legend(list(by_label.values()), list(by_label.keys())) -xlabel(r"$\psi$",fontsize=25) -ylabel(r"$2 \mu_0 / B^2$",fontsize=25) +xlabel(r"$\psi$", fontsize=25) +ylabel(r"$2 \mu_0 / B^2$", fontsize=25) fig.set_tight_layout(True) diff --git a/examples/elm-pb/data-hypre/BOUT.inp b/examples/elm-pb/data-hypre/BOUT.inp index 45dd59b536..93088f3d48 100644 --- a/examples/elm-pb/data-hypre/BOUT.inp +++ b/examples/elm-pb/data-hypre/BOUT.inp @@ -44,7 +44,7 @@ fft_measurement_flag = measure # If using FFTW, perform tests to determine fast atol = 1.0e-8 # absolute tolerance rtol = 1.0e-5 # relative tolerance -use_precon = false # Use preconditioner: User-supplied or BBD +cvode_precon_method = none # Disable CVODE preconditioning mxstep = 5000 # Number of internal steps between outputs @@ -240,4 +240,3 @@ bndry_core = neumann bndry_xin = none bndry_xout = none #bndry_target = neumann - diff --git a/examples/elm-pb/data-nonlinear/BOUT.inp b/examples/elm-pb/data-nonlinear/BOUT.inp index b61829c709..e13228ebdf 100644 --- a/examples/elm-pb/data-nonlinear/BOUT.inp +++ b/examples/elm-pb/data-nonlinear/BOUT.inp @@ -66,7 +66,7 @@ fft_measurement_flag = measure # If using FFTW, perform tests to determine fast atol = 1e-08 # absolute tolerance rtol = 1e-05 # relative tolerance -use_precon = false # Use preconditioner: User-supplied or BBD +cvode_precon_method = none # Disable CVODE preconditioning use_jacobian = false # Use user-supplied Jacobian mxstep = 5000 # Number of internal steps between outputs diff --git a/examples/elm-pb/data/BOUT.inp b/examples/elm-pb/data/BOUT.inp index a37d69a43a..43b1b6f4b0 100644 --- a/examples/elm-pb/data/BOUT.inp +++ b/examples/elm-pb/data/BOUT.inp @@ -47,7 +47,7 @@ fft_measurement_flag = measure # If using FFTW, perform tests to determine fast atol = 1e-08 # absolute tolerance rtol = 1e-05 # relative tolerance -use_precon = false # Use preconditioner: User-supplied or BBD +cvode_precon_method = none # Disable CVODE preconditioning mxstep = 5000 # Number of internal steps between outputs output_step = 1 # time between outputs diff --git a/examples/elm-pb/elm_pb.cxx b/examples/elm-pb/elm_pb.cxx index f830f3d98a..62cc970869 100644 --- a/examples/elm-pb/elm_pb.cxx +++ b/examples/elm-pb/elm_pb.cxx @@ -6,25 +6,28 @@ *******************************************************************************/ #include +#include +#include #include +#include #include +#include +#include +#include +#include +#include #include #include #include #include #include -#include +#include #include +#include #include #include -#if BOUT_HAS_HYPRE -#include -#endif - -#include - CELL_LOC loc = CELL_CENTRE; /// Set default options @@ -33,6 +36,10 @@ BOUT_OVERRIDE_DEFAULT_OPTION("phi:bndry_target", "neumann"); BOUT_OVERRIDE_DEFAULT_OPTION("phi:bndry_xin", "none"); BOUT_OVERRIDE_DEFAULT_OPTION("phi:bndry_xout", "none"); +#if BOUT_HAS_HYPRE +BOUT_OVERRIDE_DEFAULT_OPTION("laplacexy:type", "hypre"); +#endif + /// 3-field ELM simulation class ELMpb : public PhysicsModel { private: @@ -46,6 +53,7 @@ class ELMpb : public PhysicsModel { Coordinates::FieldMetric U0; // 0th vorticity of equilibrium flow, // radial flux coordinate, normalized radial flux coordinate + bool laplace_perp; // Use Laplace_perp or Delp2? bool constn0; // the total height, average width and center of profile of N0 BoutReal n0_height, n0_ave, n0_width, n0_center, n0_bottom_x, Nbar, Tibar, Tebar; @@ -184,11 +192,7 @@ class ELMpb : public PhysicsModel { bool split_n0; // Solve the n=0 component of potential -#if BOUT_HAS_HYPRE - std::unique_ptr laplacexy{nullptr}; // Laplacian solver in X-Y (n=0) -#else std::unique_ptr laplacexy{nullptr}; // Laplacian solver in X-Y (n=0) -#endif Field2D phi2D; // Axisymmetric phi @@ -203,7 +207,11 @@ class ELMpb : public PhysicsModel { bool parallel_lr_diff; // Use left and right shifted stencils for parallel differences - bool phi_constraint; // Solver for phi using a solver constraint + bool phi_constraint; // Solver for phi using a solver constraint + bool phi_boundary_relax; // Relax x boundaries of phi towards Neumann? + bool phi_core_averagey; // Average phi core boundary in Y? + BoutReal phi_boundary_timescale; // Relaxation timescale + BoutReal phi_boundary_last_update; // Time when last updated bool include_rmp; // Include RMP coil perturbation bool simple_rmp; // Just use a simple form for the perturbation @@ -230,9 +238,7 @@ class ELMpb : public PhysicsModel { int damp_width; // Width of inner damped region BoutReal damp_t_const; // Timescale of damping - // Metric coefficients - Field2D Rxy, Bpxy, Btxy, B0, hthe; - Field2D I; // Shear factor + Field2D B0; // Magnetic field const BoutReal MU0 = 4.0e-7 * PI; const BoutReal Mi = 2.0 * 1.6726e-27; // Ion mass @@ -246,8 +252,8 @@ class ELMpb : public PhysicsModel { std::unique_ptr phiSolver{nullptr}; std::unique_ptr aparSolver{nullptr}; - const Field2D N0tanh(BoutReal n0_height, BoutReal n0_ave, BoutReal n0_width, - BoutReal n0_center, BoutReal n0_bottom_x) { + Field2D N0tanh(BoutReal n0_height, BoutReal n0_ave, BoutReal n0_width, + BoutReal n0_center, BoutReal n0_bottom_x) { Field2D result; result.allocate(); @@ -299,9 +305,6 @@ class ELMpb : public PhysicsModel { protected: int init(bool restarting) override { - bool noshear; - - Coordinates* metric = mesh->getCoordinates(); output.write("Solving high-beta flute reduced equations\n"); output.write("\tFile : {:s}\n", __FILE__); @@ -311,24 +314,7 @@ class ELMpb : public PhysicsModel { // Load data from the grid // Load 2D profiles - mesh->get(J0, "Jpar0"); // A / m^2 - mesh->get(P0, "pressure"); // Pascals - - // Load curvature term - b0xcv.covariant = false; // Read contravariant components - mesh->get(b0xcv, "bxcv"); // mixed units x: T y: m^-2 z: m^-2 - - // Load metrics - if (mesh->get(Rxy, "Rxy")) { // m - throw BoutException("Error: Cannot read Rxy from grid\n"); - } - if (mesh->get(Bpxy, "Bpxy")) { // T - throw BoutException("Error: Cannot read Bpxy from grid\n"); - } - mesh->get(Btxy, "Btxy"); // T - mesh->get(B0, "Bxy"); // T - mesh->get(hthe, "hthe"); // m - mesh->get(I, "sinty"); // m^-2 T^-1 + mesh->get(P0, "pressure"); // Pascals mesh->get(Psixy, "psixy"); // get Psi mesh->get(Psiaxis, "psi_axis"); // axis flux mesh->get(Psibndry, "psi_bndry"); // edge flux @@ -350,7 +336,8 @@ class ELMpb : public PhysicsModel { ////////////////////////////////////////////////////////////// auto& globalOptions = Options::root(); auto& options = globalOptions["highbeta"]; - + laplace_perp = options["laplace_perp"].withDefault(false); + // Use Laplace_perp rather than Delp2 constn0 = options["constn0"].withDefault(true); // use the hyperbolic profile of n0. If both n0_fake_prof and // T0_fake_prof are false, use the profiles from grid file @@ -377,6 +364,9 @@ class ELMpb : public PhysicsModel { phi_constraint = options["phi_constraint"] .doc("Use solver constraint for phi?") .withDefault(false); + phi_boundary_relax = options["phi_boundary_relax"] + .doc("Relax x boundaries of phi towards Neumann?") + .withDefault(false); // Effects to include/exclude include_curvature = options["include_curvature"].withDefault(true); @@ -478,7 +468,7 @@ class ELMpb : public PhysicsModel { experiment_Er = options["experiment_Er"].withDefault(false); - noshear = options["noshear"].withDefault(false); + const bool noshear = options["noshear"].withDefault(false); relax_j_vac = options["relax_j_vac"] .doc("Relax vacuum current to zero") @@ -513,11 +503,8 @@ class ELMpb : public PhysicsModel { .withDefault(false); if (split_n0) { // Create an XY solver for n=0 component -#if BOUT_HAS_HYPRE - laplacexy = bout::utils::make_unique(mesh); -#else - laplacexy = bout::utils::make_unique(mesh); -#endif + laplacexy = LaplaceXY::create(mesh); + // Set coefficients for Boussinesq solve laplacexy->setCoefs(1.0, 0.0); phi2D = 0.0; // Starting guess @@ -661,8 +648,6 @@ class ELMpb : public PhysicsModel { Dphi0 *= -1; } - V0 = -Rxy * Bpxy * Dphi0 / B0; - if (simple_rmp) { include_rmp = true; } @@ -746,46 +731,61 @@ class ELMpb : public PhysicsModel { } } - if (!include_curvature) { + ////////////////////////////////////////////////////////////// + // Read, normalise, and set coordinates + + // Typical magnetic field + mesh->get(Bbar, "bmag", 1.0); + // Typical length scale + mesh->get(Lbar, "rmag", 1.0); + + // Read, normalise, and set coordinates + const auto tokamak_coords = + bout::set_tokamak_coordinates(*mesh, Lbar, Bbar, noshear or mesh->IncIntShear); + const auto& Rxy = tokamak_coords.Rxy; + const auto& Bpxy = tokamak_coords.Bpxy; + const auto& Btxy = tokamak_coords.Btxy; + const auto& hthe = tokamak_coords.hthe; + const auto& I = tokamak_coords.I_unnormalised; + + B0 = tokamak_coords.Bxy; + + // Need to unnormalise Rxy, as we normalise velocity separately + V0 = -(Rxy * Lbar) * Bpxy * Dphi0 / B0; + + if (include_curvature) { + // Load curvature term + b0xcv.covariant = false; // Read contravariant components + mesh->get(b0xcv, "bxcv"); // mixed units x: T y: m^-2 z: m^-2 + if (noshear) { + b0xcv.z += I * b0xcv.x; + } + } else { b0xcv = 0.0; } - if (!include_jpar0) { + if (include_jpar0) { + mesh->get(J0, "Jpar0"); // A / m^2 + } else { J0 = 0.0; } - if (noshear) { - if (include_curvature) { - b0xcv.z += I * b0xcv.x; - } - I = 0.0; - } - ////////////////////////////////////////////////////////////// // SHIFTED RADIAL COORDINATES if (mesh->IncIntShear) { // BOUT-06 style, using d/dx = d/dpsi + I * d/dz - metric->IntShiftTorsion = I; - + mesh->getCoordinates()->IntShiftTorsion = I; } else { // Dimits style, using local coordinate system if (include_curvature) { b0xcv.z += I * b0xcv.x; } - I = 0.0; // I disappears from metric } ////////////////////////////////////////////////////////////// // NORMALISE QUANTITIES - if (mesh->get(Bbar, "bmag")) { // Typical magnetic field - Bbar = 1.0; - } - if (mesh->get(Lbar, "rmag")) { // Typical length scale - Lbar = 1.0; - } - Va = sqrt(Bbar * Bbar / (MU0 * density * Mi)); Tbar = Lbar / Va; @@ -886,8 +886,8 @@ class ELMpb : public PhysicsModel { output.write(" drop K-H term\n"); } - Field2D Te; - Te = P0 / (2.0 * density * 1.602e-19); // Temperature in eV + // Temperature in eV + const Field2D Te = P0 / (2.0 * density * 1.602e-19); J0 = -MU0 * Lbar * J0 / B0; P0 = 2.0 * MU0 * P0 / (Bbar * Bbar); @@ -898,14 +898,6 @@ class ELMpb : public PhysicsModel { b0xcv.y *= Lbar * Lbar; b0xcv.z *= Lbar * Lbar; - Rxy /= Lbar; - Bpxy /= Bbar; - Btxy /= Bbar; - B0 /= Bbar; - hthe /= Lbar; - metric->dx /= Lbar * Lbar * Bbar; - I *= Lbar * Lbar * Bbar; - if (constn0) { T0_fake_prof = false; n0_fake_prof = false; @@ -1031,27 +1023,6 @@ class ELMpb : public PhysicsModel { rmp_Psi = 0.0; } - /**************** CALCULATE METRICS ******************/ - - metric->g11 = SQ(Rxy * Bpxy); - metric->g22 = 1.0 / SQ(hthe); - metric->g33 = SQ(I) * metric->g11 + SQ(B0) / metric->g11; - metric->g12 = 0.0; - metric->g13 = -I * metric->g11; - metric->g23 = -Btxy / (hthe * Bpxy * Rxy); - - metric->J = hthe / Bpxy; - metric->Bxy = B0; - - metric->g_11 = 1.0 / metric->g11 + SQ(I * Rxy); - metric->g_22 = SQ(B0 * hthe / Bpxy); - metric->g_33 = Rxy * Rxy; - metric->g_12 = Btxy * hthe * I * Rxy / Bpxy; - metric->g_13 = I * Rxy * Rxy; - metric->g_23 = Btxy * hthe * Rxy / Bpxy; - - metric->geometry(); // Calculate quantities from metric tensor - // Set B field vector B0vec.covariant = false; @@ -1148,6 +1119,24 @@ class ELMpb : public PhysicsModel { aparSolver = Laplacian::create(&globalOptions["aparSolver"], loc); + if (phi_boundary_relax) { + // Set the last update time to -1, so it will reset + // the first time RHS function is called + phi_boundary_last_update = -1.; + phi_core_averagey = options["phi_core_averagey"] + .doc("Average phi core boundary in Y?") + .withDefault(false) + and mesh->periodicY(mesh->xstart); + + phi_boundary_timescale = options["phi_boundary_timescale"] + .doc("Timescale for phi boundary relaxation [seconds]") + .withDefault(1e-7) + / Tbar; // Normalise time units to Tbar + + phiSolver->setInnerBoundaryFlags(INVERT_SET); + phiSolver->setOuterBoundaryFlags(INVERT_SET); + } + /////////////// CHECK VACUUM /////////////////////// // In vacuum region, initial vorticity should equal zero @@ -1155,7 +1144,7 @@ class ELMpb : public PhysicsModel { // Only if not restarting: Check initial perturbation // Set U to zero where P0 < vacuum_pressure - U = where(P0 - vacuum_pressure, U, 0.0); + U = where(Field2D{P0 - vacuum_pressure}, U, 0.0); if (constn0) { ubyn = U; @@ -1219,7 +1208,7 @@ class ELMpb : public PhysicsModel { // Perform communications mesh->communicate(comms); - Coordinates* metric = mesh->getCoordinates(); + const Coordinates* metric = mesh->getCoordinates(); //////////////////////////////////////////// // Transitions from 0 in core to 1 in vacuum @@ -1315,10 +1304,128 @@ class ELMpb : public PhysicsModel { Ctmp.applyBoundary(); Ctmp -= phi; // Now contains error in the boundary - C_phi = Delp2(phi) - U; // Error in the bulk + if (laplace_perp) { + C_phi = Laplace_perp(phi) - U; // Error in the bulk + } else { + C_phi = Delp2(phi) - U; // Error in the bulk + } C_phi.setBoundaryTo(Ctmp); } else { + if (phi_boundary_relax) { + // Update the boundary regions by relaxing towards zero gradient + // on a given timescale. + + if (phi_boundary_last_update < 0.0) { + // First time this has been called. + phi_boundary_last_update = t; + } else if (t > phi_boundary_last_update) { + // Only update if simulation time has advanced + // Uses an exponential decay of the weighting of the value in the boundary + // so that the solution is well behaved for arbitrary steps + const BoutReal weight = + exp(-(t - phi_boundary_last_update) / phi_boundary_timescale); + phi_boundary_last_update = t; + + if (mesh->firstX()) { + BoutReal phivalue = 0.0; + if (phi_core_averagey) { + // Calculate a single phi boundary value for all Y slices + BoutReal philocal = 0.0; + for (int j = mesh->ystart; j <= mesh->yend; j++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { + philocal += phi(mesh->xstart, j, k); + } + } + MPI_Comm comm_inner = mesh->getYcomm(0); + int np = 0; + MPI_Comm_size(comm_inner, &np); + MPI_Allreduce(&philocal, &phivalue, 1, MPI_DOUBLE, MPI_SUM, comm_inner); + phivalue /= (np * mesh->LocalNz * mesh->LocalNy); + } + for (int j = mesh->ystart; j <= mesh->yend; j++) { + if (!phi_core_averagey) { + phivalue = 0.0; // Calculate phi boundary for each Y index separately + for (int k = mesh->zstart; k <= mesh->zend; k++) { + phivalue += phi(mesh->xstart, j, k); + } + phivalue /= mesh->LocalNz; // Average in Z of point next to boundary + } + + // Old value of phi at boundary. Note: this is constant in Z + const BoutReal oldvalue = + 0.5 * (phi(mesh->xstart - 1, j, 0) + phi(mesh->xstart, j, 0)); + + // New value of phi at boundary, relaxing towards phivalue + const BoutReal newvalue = weight * oldvalue + (1. - weight) * phivalue; + + // Set phi at the boundary to this value + for (int k = mesh->zstart; k <= mesh->zend; k++) { + phi(mesh->xstart - 1, j, k) = 2. * newvalue - phi(mesh->xstart, j, k); + phi(mesh->xstart - 2, j, k) = phi(mesh->xstart - 1, j, k); + } + } + } + + if (mesh->lastX()) { + for (int j = mesh->ystart; j <= mesh->yend; j++) { + BoutReal phivalue = 0.0; + for (int k = mesh->zstart; k <= mesh->zend; k++) { + phivalue += phi(mesh->xend, j, k); + } + phivalue /= mesh->LocalNz; // Average in Z of point next to boundary + + // Old value of phi at boundary. Note: this is constant in Z + BoutReal oldvalue = + 0.5 * (phi(mesh->xend + 1, j, 0) + phi(mesh->xend, j, 0)); + + // New value of phi at boundary, relaxing towards phivalue + const BoutReal newvalue = weight * oldvalue + (1. - weight) * phivalue; + + // Set phi at the boundary to this value + for (int k = mesh->zstart; k <= mesh->zend; k++) { + phi(mesh->xend + 1, j, k) = 2. * newvalue - phi(mesh->xend, j, k); + phi(mesh->xend + 2, j, k) = phi(mesh->xend + 1, j, k); + } + } + } + } + } + + Field3D phi_shift = phi; + if (constn0 and diamag) { + // Solving for phi + ion pressure term + phi_shift += 0.5 * dnorm * P / B0; + } else { + // Ensure that memory is not shared between phi and phi_shift + phi_shift.allocate(); + } + + // Update boundary conditions. + // The INVERT_SET flag takes the value in the guard (boundary) cell + // and sets the boundary between cells to this value. + // This shift by 1/2 grid cell is important. + + if (mesh->firstX()) { + for (int j = mesh->ystart; j <= mesh->yend; j++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { + // Average phi + Pi at the boundary, and set the boundary cell + // to this value. The phi solver will then put the value back + // onto the cell mid-point + phi_shift(mesh->xstart - 1, j, k) = + 0.5 * (phi_shift(mesh->xstart - 1, j, k) + phi_shift(mesh->xstart, j, k)); + } + } + } + + if (mesh->lastX()) { + for (int j = mesh->ystart; j <= mesh->yend; j++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { + phi_shift(mesh->xend + 1, j, k) = + 0.5 * (phi_shift(mesh->xend + 1, j, k) + phi_shift(mesh->xend, j, k)); + } + } + } if (constn0) { if (split_n0) { @@ -1326,44 +1433,84 @@ class ELMpb : public PhysicsModel { // Boussinesq, split // Split into axisymmetric and non-axisymmetric components Field2D Vort2D = DC(U); // n=0 component + Field2D phi_shift_2d = phi2D; + + if (phi_boundary_relax) { + phi_shift_2d = DC(phi_shift); + } + phi_shift -= phi_shift_2d; // Applies boundary condition for "phi". phi2D.applyBoundary(t); // Solve axisymmetric (n=0) part - phi2D = laplacexy->solve(Vort2D, phi2D); + phi2D = laplacexy->solve(Vort2D, phi_shift_2d); // Solve non-axisymmetric part - phi = phiSolver->solve(U - Vort2D); + phi = phiSolver->solve(U - Vort2D, phi_shift); phi += phi2D; // Add axisymmetric part } else { - phi = phiSolver->solve(U); + if (phi_boundary_relax) { + phi = phiSolver->solve(U, phi_shift); + } else { + phi = phiSolver->solve(U); + } } - if (diamag) { phi -= 0.5 * dnorm * P / B0; } } else { ubyn = U / N0; if (diamag) { - ubyn -= 0.5 * dnorm / (N0 * B0) * Delp2(P); + if (laplace_perp) { + ubyn -= 0.5 * dnorm / (N0 * B0) * Laplace_perp(P); + } else { + ubyn -= 0.5 * dnorm / (N0 * B0) * Delp2(P); + } mesh->communicate(ubyn); } // Invert laplacian for phi phiSolver->setCoefC(N0); phi = phiSolver->solve(ubyn); } - // Apply a boundary condition on phi for target plates - phi.applyBoundary(); mesh->communicate(phi); } + if (mesh->firstX()) { + for (int i = mesh->xstart - 2; i >= 0; --i) { + for (int j = mesh->ystart; j <= mesh->yend; ++j) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { + phi(i, j, k) = phi(i + 1, j, k); + } + } + } + } + + if (mesh->lastX()) { + for (int i = mesh->xend + 2; i < mesh->LocalNx; ++i) { + for (int j = mesh->ystart; j <= mesh->yend; ++j) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { + phi(i, j, k) = phi(i - 1, j, k); + } + } + } + } + if (!evolve_jpar) { // Get J from Psi - Jpar = Delp2(Psi); + if (laplace_perp) { + Jpar = Laplace_perp(Psi); + } else { + Jpar = Delp2(Psi); + } + if (include_rmp) { - Jpar += Delp2(rmp_Psi); + if (laplace_perp) { + Jpar += Laplace_perp(rmp_Psi); + } else { + Jpar += Delp2(rmp_Psi); + } } Jpar.applyBoundary(); @@ -1397,8 +1544,11 @@ class ELMpb : public PhysicsModel { } // Get Delp2(J) from J - Jpar2 = Delp2(Jpar); - + if (laplace_perp) { + Jpar2 = Laplace_perp(Jpar); + } else { + Jpar2 = Delp2(Jpar); + } Jpar2.applyBoundary(); mesh->communicate(Jpar2); @@ -1443,7 +1593,7 @@ class ELMpb : public PhysicsModel { for (int jz = 0; jz < mesh->LocalNz; jz++) { // Zero-gradient potential - BoutReal const phisheath = phi_fa(r.ind, mesh->ystart, jz); + const BoutReal phisheath = phi_fa(r.ind, mesh->ystart, jz); BoutReal jsheath = -(sqrt(mi_me) / (2. * sqrt(PI))) * phisheath; @@ -1464,7 +1614,7 @@ class ELMpb : public PhysicsModel { for (int jz = 0; jz < mesh->LocalNz; jz++) { // Zero-gradient potential - BoutReal const phisheath = phi_fa(r.ind, mesh->yend, jz); + const BoutReal phisheath = phi_fa(r.ind, mesh->yend, jz); BoutReal jsheath = (sqrt(mi_me) / (2. * sqrt(PI))) * phisheath; @@ -1494,7 +1644,11 @@ class ELMpb : public PhysicsModel { // Jpar Field3D B0U = B0 * U; mesh->communicate(B0U); - ddt(Jpar) = -Grad_parP(B0U, loc) / B0 + eta * Delp2(Jpar); + if (laplace_perp) { + ddt(Jpar) = -Grad_parP(B0U, loc) / B0 + eta * Laplace_perp(Jpar); + } else { + ddt(Jpar) = -Grad_parP(B0U, loc) / B0 + eta * Delp2(Jpar); + } if (relax_j_vac) { // Make ddt(Jpar) relax to zero. @@ -1524,11 +1678,19 @@ class ELMpb : public PhysicsModel { } if (hyperresist > 0.0) { // Hyper-resistivity - ddt(Psi) -= eta * hyperresist * Delp2(Jpar); + if (laplace_perp) { + ddt(Psi) -= eta * hyperresist * Laplace_perp(Jpar); + } else { + ddt(Psi) -= eta * hyperresist * Delp2(Jpar); + } } if (ehyperviscos > 0.0) { // electron Hyper-viscosity coefficient - ddt(Psi) -= eta * ehyperviscos * Delp2(Jpar2); + if (laplace_perp) { + ddt(Psi) -= eta * ehyperviscos * Laplace_perp(Jpar2); + } else { + ddt(Psi) -= eta * ehyperviscos * Delp2(Jpar2); + } } // Parallel hyper-viscous diffusion for vector potential @@ -1542,10 +1704,10 @@ class ELMpb : public PhysicsModel { // Vacuum solution if (relax_j_vac) { // Calculate the J and Psi profile we're aiming for - Field3D Jtarget = Jpar * (1.0 - vac_mask); // Zero in vacuum + const Field3D Jtarget = Jpar * (1.0 - vac_mask); // Zero in vacuum // Invert laplacian for Psi - Field3D Psitarget = aparSolver->solve(Jtarget); + const Field3D Psitarget = aparSolver->solve(Jtarget); // Add a relaxation term in the vacuum ddt(Psi) = @@ -1599,7 +1761,11 @@ class ELMpb : public PhysicsModel { } if (viscos_perp > 0.0) { - ddt(U) += viscos_perp * Delp2(U); // Perpendicular viscosity + if (laplace_perp) { + ddt(U) += viscos_perp * Laplace_perp(U); // Perpendicular viscosity + } else { + ddt(U) += viscos_perp * Delp2(U); // Perpendicular viscosity + } } // Hyper-viscosity @@ -1626,21 +1792,39 @@ class ELMpb : public PhysicsModel { Pi = 0.5 * P; Pi0 = 0.5 * P0; - Dperp2Phi0 = Field3D(Delp2(B0 * phi0)); - Dperp2Phi0.applyBoundary(); - mesh->communicate(Dperp2Phi0); + if (laplace_perp) { + Dperp2Phi0 = Field3D(Laplace_perp(B0 * phi0)); + Dperp2Phi0.applyBoundary(); + mesh->communicate(Dperp2Phi0); + + Dperp2Phi = Laplace_perp(B0 * phi); + Dperp2Phi.applyBoundary(); + mesh->communicate(Dperp2Phi); + + Dperp2Pi0 = Field3D(Laplace_perp(Pi0)); + Dperp2Pi0.applyBoundary(); + mesh->communicate(Dperp2Pi0); - Dperp2Phi = Delp2(B0 * phi); - Dperp2Phi.applyBoundary(); - mesh->communicate(Dperp2Phi); + Dperp2Pi = Laplace_perp(Pi); + Dperp2Pi.applyBoundary(); + mesh->communicate(Dperp2Pi); + } else { + Dperp2Phi0 = Field3D(Delp2(B0 * phi0)); + Dperp2Phi0.applyBoundary(); + mesh->communicate(Dperp2Phi0); + + Dperp2Phi = Delp2(B0 * phi); + Dperp2Phi.applyBoundary(); + mesh->communicate(Dperp2Phi); - Dperp2Pi0 = Field3D(Delp2(Pi0)); - Dperp2Pi0.applyBoundary(); - mesh->communicate(Dperp2Pi0); + Dperp2Pi0 = Field3D(Delp2(Pi0)); + Dperp2Pi0.applyBoundary(); + mesh->communicate(Dperp2Pi0); - Dperp2Pi = Delp2(Pi); - Dperp2Pi.applyBoundary(); - mesh->communicate(Dperp2Pi); + Dperp2Pi = Delp2(Pi); + Dperp2Pi.applyBoundary(); + mesh->communicate(Dperp2Pi); + } bracketPhi0P = bracket(B0 * phi0, Pi, bm_exb); bracketPhi0P.applyBoundary(); @@ -1654,12 +1838,17 @@ class ELMpb : public PhysicsModel { ddt(U) -= 0.5 * Upara2 * bracket(Pi0, Dperp2Phi, bm_exb) / B0; Field3D B0phi = B0 * phi; mesh->communicate(B0phi); - Field3D B0phi0 = B0 * phi0; + Field2D B0phi0 = B0 * phi0; mesh->communicate(B0phi0); ddt(U) += 0.5 * Upara2 * bracket(B0phi, Dperp2Pi0, bm_exb) / B0; ddt(U) += 0.5 * Upara2 * bracket(B0phi0, Dperp2Pi, bm_exb) / B0; - ddt(U) -= 0.5 * Upara2 * Delp2(bracketPhi0P) / B0; - ddt(U) -= 0.5 * Upara2 * Delp2(bracketPhiP0) / B0; + if (laplace_perp) { + ddt(U) -= 0.5 * Upara2 * Laplace_perp(bracketPhi0P) / B0; + ddt(U) -= 0.5 * Upara2 * Laplace_perp(bracketPhiP0) / B0; + } else { + ddt(U) -= 0.5 * Upara2 * Delp2(bracketPhi0P) / B0; + ddt(U) -= 0.5 * Upara2 * Delp2(bracketPhiP0) / B0; + } if (nonlinear) { Field3D B0phi = B0 * phi; @@ -1670,7 +1859,11 @@ class ELMpb : public PhysicsModel { ddt(U) -= 0.5 * Upara2 * bracket(Pi, Dperp2Phi, bm_exb) / B0; ddt(U) += 0.5 * Upara2 * bracket(B0phi, Dperp2Pi, bm_exb) / B0; - ddt(U) -= 0.5 * Upara2 * Delp2(bracketPhiP) / B0; + if (laplace_perp) { + ddt(U) -= 0.5 * Upara2 * Laplace_perp(bracketPhiP) / B0; + } else { + ddt(U) -= 0.5 * Upara2 * Delp2(bracketPhiP) / B0; + } } } @@ -1808,7 +2001,13 @@ class ELMpb : public PhysicsModel { int precon(BoutReal UNUSED(t), BoutReal gamma, BoutReal UNUSED(delta)) { // First matrix, applying L mesh->communicate(ddt(Psi)); - Field3D Jrhs = Delp2(ddt(Psi)); + ddt(Psi).applyBoundary("neumann"); + Field3D Jrhs; + if (laplace_perp) { + Jrhs = Laplace_perp(ddt(Psi)); + } else { + Jrhs = Delp2(ddt(Psi)); + } Jrhs.applyBoundary("neumann"); if (jpar_bndry_width > 0) { @@ -1834,9 +2033,11 @@ class ELMpb : public PhysicsModel { } mesh->communicate(Jrhs, ddt(P)); + ddt(P).applyBoundary("neumann"); Field3D U1 = ddt(U); - U1 += (gamma * B0 * B0) * Grad_par(Jrhs, CELL_CENTRE) + (gamma * b0xcv) * Grad(P); + U1 += + (gamma * B0 * B0) * Grad_par(Jrhs, CELL_CENTRE) + (gamma * b0xcv) * Grad(ddt(P)); // Second matrix, solving Alfven wave dynamics static std::unique_ptr invU{nullptr}; @@ -1880,8 +2081,11 @@ class ELMpb : public PhysicsModel { phi = phiSolver->solve(ddt(U)); - Jpar = Delp2(ddt(Psi)); - + if (laplace_perp) { + Jpar = Laplace_perp(ddt(Psi)); + } else { + Jpar = Delp2(ddt(Psi)); + } mesh->communicate(phi, Jpar); Field3D JP = -b0xGrad_dot_Grad(phi, P0); diff --git a/examples/elm-pb/relaxing/BOUT.inp b/examples/elm-pb/relaxing/BOUT.inp new file mode 100644 index 0000000000..cd911417b2 --- /dev/null +++ b/examples/elm-pb/relaxing/BOUT.inp @@ -0,0 +1,298 @@ +# settings file for BOUT++ +# High-Beta reduced MHD case + +################################################## +# Global settings used by the core code + +nout = 1000 # number of time-steps +timestep = 1 # time between outputs + +zperiod = 5 # Fraction of a torus to simulate +MZ = 64 # Number of points in Z + +grid = "cbm18_dens8.grid_nx68ny64.nc" #"cbm18_dens3_0.5BS_516nx64ny.grid.nc" + +[mesh] + +staggergrids = false # Use staggered grids + +[mesh:paralleltransform] +#type = shifted # Use shifted metric method +type = shiftedinterp + +################################################## +# derivative methods + +[mesh:ddx] +first = C4 # order of first x derivatives +second = C4 # order of second x derivatives +upwind = W3 # order of upwinding method W3 = Weno3 + +[mesh:ddy] +first = C4 +second = C4 +upwind = W3 +flux = C2 + +[mesh:ddz] +first = C4 # Z derivatives can be done using FFT +second = C4 +upwind = W3 + +################################################## +# FFTs + +[fft] + +fft_measurement_flag = measure # If using FFTW, perform tests to determine fastest method + +[output] + +#type = adios +#shiftoutput = true # Put the output into field-aligned coordinates + +[restart_files] + +#type = adios + +[laplace] + +#type = hypre3d +#rtol = 1.e-9 +#atol = 1.e-14 +#pctype = sor # Preconditioner + +#atol = 1e-12 +#rtol = 1e-08 + + +################################################## +# Solver settings + +[solver] + +# mudq, mldq, mukeep, mlkeep preconditioner options +atol = 1.0e-8 # absolute tolerance +rtol = 1.0e-5 # relative tolerance + +cvode_precon_method = none # Disable CVODE preconditioning + +mxstep = 50000 # Number of internal steps between outputs + +################################################## +# settings for high-beta reduced MHD + +[highbeta] + +density = 1.0e19 # number density of deuterium [m^-3] + # used to produce output normalisations +constn0 = true +n0_fake_prof = false + +sheath_boundaries = false + +evolve_jpar = false # If true, evolve J raher than Psi + +evolve_pressure = true # If false, switch off all pressure evolution + +phi_constraint = false # Solve phi as a constraint (DAE system, needs IDA) + +## Effects to include/exclude + +include_jpar0 = true # determines whether to include jpar0 terms +include_curvature = true # include curvature drive term? + +compress = false # set compressible (evolve Vpar) +nonlinear = true # include non-linear terms? + +diamag = true # Include diamagnetic effects? +diamag_grad_t = false # Include Grad_par(Te) term in Psi equation +diamag_phi0 = true # Balance ExB against Vd for stationary equilibrium + +split_n0 = false + +laplace_perp = true + +################################################## +# BRACKET_METHOD flags: +# 0:BRACKET_STD; derivative methods will be determined +# by the choices C or W in this input file +# 1:BRACKET_SIMPLE; 2:BRACKET_ARAKAWA; 3:BRACKET_CTU. + +bm_exb_flag = 0 +bm_mag_flag = 0 +################################################################## +withflow = false # With flow or not +D_0 = 130000 # differential potential +D_s = 20 # shear parameter +K_H_term = false # Contain K-H term +sign = -1 # flow direction +x0 = 0.855 # peak location +D_min = 3000 # constant +################################################################## + +eHall = false # Include electron pressure effects in Ohm's law? +AA = 2.0 # ion mass in units of proton mass +Zeff = 1.0 +#Zi = 1.0 + +noshear = false # zero all shear + +relax_j_vac = false # Relax to zero-current in the vacuum +relax_j_tconst = 1e-2 # Time constant for vacuum relaxation + +## Toroidal filtering +filter_z = false # remove all except one mode +filter_z_mode = 1 # Specify which harmonic to keep (1 = fundamental) +low_pass_z = 16 # Keep up to and including this harmonic (-1 = keep all) +zonal_flow = true # keep zonal component of vorticity? +zonal_field = false # keep zonal component of Psi? +zonal_bkgd = true # keep zonal component of P? + +## Jpar smoothing +smooth_j_x = true # Filter Jpar in the X direction + +## Magnetic perturbations +include_rmp = false # Read RMP data from grid file + +simple_rmp = false # Enable/disable a simple model of RMP +rmp_n = 3 # Toroidal mode number +rmp_m = 6 # Poloidal mode number +rmp_factor = 1.e-4 # Amplitude of Apar [Tm] +rmp_ramp = 1.e-4 # Timescale [s] of ramp +rmp_polwid = -1.0 # Width of Gaussian factor (< 0 = No Gaussian) +rmp_polpeak = 0.5 # Y location of maximum (fraction) + +## Vacuum region control + +vacuum_pressure = 0.02 # the pressure below which it is considered vacuum + # fraction of peak pressure +vacuum_trans = 0.01 # transition width (fraction of P) + +## Resistivity and Hyper-resistivity + +vac_lund = 1.0e8 # Lundquist number in vacuum (negative -> infinity) +core_lund = 1.0e8 # Lundquist number in core (negative -> infinity) +hyperresist = 1.e-4 # Hyper-resistivity coefficient (like 1 / Lundquist number) + +## Inner boundary damping + +damp_width = -1 # Width of damping region (grid cells) +damp_t_const = 1e-2 # Damping time constant + +## Parallel pressure diffusion + +diffusion_par = 1.0e-2 # Parallel pressure diffusion (< 0 = none) +diffusion_p4 = -1e-05 # parallel hyper-viscous diffusion for pressure (< 0 = none) +diffusion_u4 = -1e-05 # parallel hyper-viscous diffusion for vorticity (< 0 = none) +diffusion_a4 = -1e-05 # parallel hyper-viscous diffusion for vector potential (< 0 = none) + +## heat source in pressure in watts + +heating_P = -1 # heat power in watts (< 0 = none) +hp_width = 0.1 # heat width, in percentage of nx (< 0 = none) +hp_length = 0.3 # heat length in percentage of nx (< 0 = none) + +## sink rate in pressure + +sink_P = -1 # sink rate in pressure (< 0 = none) +sp_width = 0.04 # sink width, in percentage of nx (< 0 = none) +sp_length = 0.15 # sink length in percentage of nx (< 0 = none) + + +## left edge sink rate in vorticity +sink_Ul = 10.0 # left edge sink rate in vorticity (< 0 = none) +su_widthl = 0.06 # left edge sink width, in percentage of nx (< 0 = none) +su_lengthl = 0.1 # left edge sink length in percentage of nx (< 0 = none) + +## right edge sink rate in vorticity +sink_Ur = 10.0 # right edge sink rate in vorticity (< 0 = none) +su_widthr = 0.06 # right edge sink width, in percentage of nx (< 0 = none) +su_lengthr = 0.1 # right edge sink length in percentage of nx (< 0 = none) + +## Viscosity and Hyper-viscosity + +viscos_par = 0.1 # Parallel viscosity (< 0 = none) +viscos_perp = 1.0e-7 # Perpendicular viscosity (< 0 = none) +hyperviscos = -1.0 # Radial hyper viscosity + +## Compressional terms (only when compress = true) +phi_curv = true # Include curvature*Grad(phi) in P equation +# gamma = 1.6666 + +phi_boundary_relax = true # Relax phi at radial boundaries towards zero gradient +phi_core_averagey = false +phi_boundary_timescale = 1.0e-6 # In seconds + +[phiSolver] +# INVERT_DC_GRAD = 1 // Zero-gradient for DC (constant in Z) component. Default is zero value +# INVERT_AC_GRAD = 2 // Zero-gradient for AC (non-constant in Z) component. Default is zero value +# INVERT_AC_LAP = 4 // Use zero-laplacian (decaying solution) to AC component +# INVERT_DC_LAP = 64 // Use zero-laplacian solution for DC component +#type = hypre3d +#rtol = 1.e-9 +#atol = 1.e-14 +#inner_boundary_flags = 0 # 0 for Dirichlet; 2 for Neumann +#outer_boundary_flags = 0 # 0 for Dirichlet; 2 for Neumann +#hypre_print_level = 1 # print information on the matrices, solver settings, and iterations. + +[aparSolver] +#type = hypre3d +#rtol = 1.e-9 +#atol = 1.e-14 +#inner_boundary_flags = 4 # INVERT_AC_LAP +#outer_boundary_flags = 1 + 4 # INVERT_DC_GRAD + INVERT_AC_LAP + +################################################## +# settings for individual variables +# The section "All" defines default settings for all variables +# These can be overridden for individual variables in +# a section of that name. + +[all] +scale = 0.0 # default size of initial perturbations + +# boundary conditions +# ------------------- +# dirichlet - Zero value +# neumann - Zero gradient +# zerolaplace - Laplacian = 0, decaying solution +# constlaplace - Laplacian = const, decaying solution +# +# relax( ) - Make boundary condition relaxing + +bndry_all = dirichlet_o2 # Default to zero-value + +[U] # vorticity +scale = 1e-05 +function = ballooning(gauss(x-0.5,0.1)*gauss(y-pi,0.6*pi)*sin(z),3) + +[P] # pressure +bndry_core = dirichlet +#scale = 1.0e-5 + +[Psi] # Vector potential + +# zero laplacian +bndry_xin = zerolaplace +bndry_xout = zerolaplace + +[Psi_loc] # for staggering + +bndry_xin = zerolaplace +bndry_xout = zerolaplace + +bndry_yup = free_o3 +bndry_ydown = free_o3 + +[J] # parallel current + +# Zero gradient in the core +bndry_core = neumann + +[phi] + +#bndry_core = neumann +bndry_xin = none +bndry_xout = none +#bndry_target = neumann diff --git a/examples/elm-pb/runexample.py b/examples/elm-pb/runexample.py index f7ebc01028..b49902d29c 100755 --- a/examples/elm-pb/runexample.py +++ b/examples/elm-pb/runexample.py @@ -28,18 +28,22 @@ # Calculate RMS in toroidal direction prms = np.sqrt(np.mean(p**2, axis=3)) -growth = np.gradient(np.log(prms[:,42,32])) +growth = np.gradient(np.log(prms[:, 42, 32])) # Final growth-rate gamma = growth[-2] import matplotlib.pyplot as plt -plt.plot(tarr, prms[:,42,32], label='Outboard midplane') -plt.plot( [tarr[0], tarr[-1]], - [prms[-1,42,32]*np.exp(gamma*(tarr[0] - tarr[-1])), prms[-1,42,32]], '--', label=r'$\gamma =$'+str(gamma)) +plt.plot(tarr, prms[:, 42, 32], label="Outboard midplane") +plt.plot( + [tarr[0], tarr[-1]], + [prms[-1, 42, 32] * np.exp(gamma * (tarr[0] - tarr[-1])), prms[-1, 42, 32]], + "--", + label=r"$\gamma =$" + str(gamma), +) -plt.yscale('log') +plt.yscale("log") plt.grid() plt.xlabel(r"Time [$1/\tau_A$]") @@ -57,19 +61,21 @@ # Take a poloidal slice at fixed toroidal angle from boutdata.pol_slice import pol_slice -p2d = pol_slice(p[-1,:,:,:], 'cbm18_dens8.grid_nx68ny64.nc', n=15, zangle=0.0) + +p2d = pol_slice(p[-1, :, :, :], "cbm18_dens8.grid_nx68ny64.nc", n=15, zangle=0.0) # Read grid file to get coordinates from boututils.datafile import DataFile -g = DataFile('cbm18_dens8.grid_nx68ny64.nc') -Rxy = g.read("Rxy") # Major radius [m] -Zxy = g.read("Zxy") # Height [m] +g = DataFile("cbm18_dens8.grid_nx68ny64.nc") + +Rxy = g.read("Rxy") # Major radius [m] +Zxy = g.read("Zxy") # Height [m] plt.contourf(Rxy, Zxy, p2d, 30) -plt.axis('equal') # Maintain aspect ratio +plt.axis("equal") # Maintain aspect ratio -plt.colorbar() # Plot a bar down the side with a color scale +plt.colorbar() # Plot a bar down the side with a color scale plt.savefig("poloidal_slice.pdf") diff --git a/examples/elm-pb/test/BOUT.inp b/examples/elm-pb/test/BOUT.inp index 63f3d96f2b..76ccf14888 100644 --- a/examples/elm-pb/test/BOUT.inp +++ b/examples/elm-pb/test/BOUT.inp @@ -79,7 +79,7 @@ upwind = W3 atol = 1e-08 # absolute tolerance rtol = 1e-05 # relative tolerance -use_precon = false # Use preconditioner: User-supplied or BBD +cvode_precon_method = none # Disable CVODE preconditioning use_jacobian = false # Use user-supplied Jacobian mxstep = 5000 # Number of internal steps between outputs diff --git a/examples/fci-wave/CMakeLists.txt b/examples/fci-wave/CMakeLists.txt index 2680b1310e..ea299edb5a 100644 --- a/examples/fci-wave/CMakeLists.txt +++ b/examples/fci-wave/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(fci-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(fci-wave +bout_add_example( + fci-wave SOURCES fci-wave.cxx DATA_DIRS div div-integrate logn - EXTRA_FILES compare-density.py) + EXTRA_FILES compare-density.py +) diff --git a/examples/fci-wave/compare-density.py b/examples/fci-wave/compare-density.py index c039c250b6..16e620a08f 100644 --- a/examples/fci-wave/compare-density.py +++ b/examples/fci-wave/compare-density.py @@ -1,4 +1,3 @@ - import matplotlib.pyplot as plt from boutdata import collect import numpy as np @@ -9,60 +8,61 @@ # Note: Data from fci-wave-logn examples commented out. data_noboundary = [ - ("div", "Model 1 (density, point interpolation)") - ,("div-integrate", "Model 2 (density, area integration)") - ,("logn", "Model 3 (log density, area integration)") - #,("../fci-wave-logn/div-integrate", "Model 5 (velocity, log density, area integration)") + ("div", "Model 1 (density, point interpolation)"), + ("div-integrate", "Model 2 (density, area integration)"), + ("logn", "Model 3 (log density, area integration)"), + # ,("../fci-wave-logn/div-integrate", "Model 5 (velocity, log density, area integration)") ] data_boundary = [ - ("boundary", "Model 2 (density, momentum)") - ,("boundary-logn", "Model 3 (log density, momentum)") - #,("../fci-wave-logn/boundary", "Model 5 (log density, velocity)") - ] + ("boundary", "Model 2 (density, momentum)"), + ("boundary-logn", "Model 3 (log density, momentum)"), + # ,("../fci-wave-logn/boundary", "Model 5 (log density, velocity)") +] # Change this to select no boundary or boundary cases data = data_noboundary if run: from boututils.run_wrapper import shell_safe, launch_safe + shell_safe("make > make.log") - for path,label in data: - launch_safe("./fci-wave -d "+path, nproc=nproc, pipe=False) + for path, label in data: + launch_safe("./fci-wave -d " + path, nproc=nproc, pipe=False) -# Collect the results into a dictionary +# Collect the results into a dictionary sum_n_B = {} -for path,label in data: +for path, label in data: n = collect("n", path=path) Bxyz = collect("Bxyz", path=path) time = collect("t_array", path=path) - + nt, nx, ny, nz = n.shape - + n_B = np.ndarray(nt) for t in range(nt): - n_B[t] = np.sum(n[t,:,:,:] / Bxyz) + n_B[t] = np.sum(n[t, :, :, :] / Bxyz) sum_n_B[path] = (time, n_B) # Plot the density at the final time - + plt.figure() - plt.contourf(n[-1,:,0,:].T, 100) + plt.contourf(n[-1, :, 0, :].T, 100) plt.colorbar() plt.xlabel("Major radius") plt.ylabel("Height") - plt.title("Density n, "+label) - plt.savefig(path+".pdf") + plt.title("Density n, " + label) + plt.savefig(path + ".pdf") plt.show() # Make a plot comparing total sum density / B - + plt.figure() -for path,label in data: +for path, label in data: time, n_B = sum_n_B[path] plt.plot(time, n_B, label=label) plt.legend() @@ -71,4 +71,3 @@ plt.savefig("compare-density.pdf") plt.show() - diff --git a/examples/finite-volume/diffusion/CMakeLists.txt b/examples/finite-volume/diffusion/CMakeLists.txt index 0dd7d220f6..ccc3d0f7e0 100644 --- a/examples/finite-volume/diffusion/CMakeLists.txt +++ b/examples/finite-volume/diffusion/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(finite-volume-diffusion LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(finite-volume-diffusion +bout_add_example( + finite-volume-diffusion SOURCES diffusion.cxx - EXTRA_FILES mms.py) + EXTRA_FILES mms.py +) diff --git a/examples/finite-volume/diffusion/mms.py b/examples/finite-volume/diffusion/mms.py index 2c609ca82e..31b7714727 100644 --- a/examples/finite-volume/diffusion/mms.py +++ b/examples/finite-volume/diffusion/mms.py @@ -5,26 +5,26 @@ from math import pi # Length of the y domain -Ly = 10. +Ly = 10.0 # metric tensor metric = Metric() # Identity # Define solution in terms of input x,y,z -f = 1 + 0.1*sin(2*y - t) -k = 1 + 0.1*sin(y) +f = 1 + 0.1 * sin(2 * y - t) +k = 1 + 0.1 * sin(y) # Turn solution into real x and z coordinates -replace = [ (y, metric.y*2*pi/Ly) ] +replace = [(y, metric.y * 2 * pi / Ly)] f = f.subs(replace) -k = k.subs(replace) +k = k.subs(replace) ############################## # Calculate time derivatives -dfdt = Div_par( k * Grad_par(f) ) +dfdt = Div_par(k * Grad_par(f)) ############################# # Calculate sources @@ -32,7 +32,7 @@ Sf = diff(f, t) - dfdt # Substitute back to get input y coordinates -replace = [ (metric.y, y*Ly/(2*pi) ) ] +replace = [(metric.y, y * Ly / (2 * pi))] k = k.subs(replace) f = f.subs(replace) diff --git a/examples/finite-volume/fluid/CMakeLists.txt b/examples/finite-volume/fluid/CMakeLists.txt index e9028459ec..e798da77fe 100644 --- a/examples/finite-volume/fluid/CMakeLists.txt +++ b/examples/finite-volume/fluid/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(finite-volume-fluid LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(finite-volume-fluid +bout_add_example( + finite-volume-fluid SOURCES fluid.cxx DATA_DIRS data mms - EXTRA_FILES mms.py) + EXTRA_FILES mms.py +) diff --git a/examples/finite-volume/fluid/README.md b/examples/finite-volume/fluid/README.md index d112da3039..583277d130 100644 --- a/examples/finite-volume/fluid/README.md +++ b/examples/finite-volume/fluid/README.md @@ -51,5 +51,10 @@ for the first order upwinding method, or for the second order MinMod method. +Or, to use a smooth symmetric limiter: + FV::Div_par +Or a higher-order smooth WENO reconstruction: + + FV::Div_par diff --git a/examples/finite-volume/fluid/mms.py b/examples/finite-volume/fluid/mms.py index 8ed8fba517..a31782e2c7 100644 --- a/examples/finite-volume/fluid/mms.py +++ b/examples/finite-volume/fluid/mms.py @@ -5,19 +5,19 @@ from math import pi # Length of the y domain -Ly = 10. +Ly = 10.0 # metric tensor metric = Metric() # Identity # Define solution in terms of input x,y,z -n = 1 + 0.1*sin(2*y - t) -p = 1 + 0.1*cos(3*y + t) -nv = 0.1*sin(y + 2*t) +n = 1 + 0.1 * sin(2 * y - t) +p = 1 + 0.1 * cos(3 * y + t) +nv = 0.1 * sin(y + 2 * t) # Turn solution into real x and z coordinates -replace = [ (y, metric.y*2*pi/Ly) ] +replace = [(y, metric.y * 2 * pi / Ly)] n = n.subs(replace) p = p.subs(replace) @@ -27,16 +27,16 @@ # Calculate time derivatives v = nv / n -gamma = 5./3 +gamma = 5.0 / 3 # Density equation -dndt = - Div_par(nv) +dndt = -Div_par(nv) # Pressure equation -dpdt = - Div_par(p*v) - (gamma-1.0)*p*Div_par(v) +dpdt = -Div_par(p * v) - (gamma - 1.0) * p * Div_par(v) # Momentum equation -dnvdt = - Div_par(nv*v) - Grad_par(p) +dnvdt = -Div_par(nv * v) - Grad_par(p) ############################# # Calculate sources @@ -46,7 +46,7 @@ Snv = diff(nv, t) - dnvdt # Substitute back to get input y coordinates -replace = [ (metric.y, y*Ly/(2*pi) ) ] +replace = [(metric.y, y * Ly / (2 * pi))] n = n.subs(replace) p = p.subs(replace) diff --git a/examples/finite-volume/test/CMakeLists.txt b/examples/finite-volume/test/CMakeLists.txt index 73fe99f960..d09567e193 100644 --- a/examples/finite-volume/test/CMakeLists.txt +++ b/examples/finite-volume/test/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(finite-volume-test LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/gas-compress/CMakeLists.txt b/examples/gas-compress/CMakeLists.txt index 1b4416d32b..75978ba584 100644 --- a/examples/gas-compress/CMakeLists.txt +++ b/examples/gas-compress/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(gas-compress LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(gas-compress +bout_add_example( + gas-compress SOURCES gas_compress.cxx gas_compress.hxx DATA_DIRS rayleigh-taylor sod-shock - EXTRA_FILES rt.grd.nc sod.grd.nc) + EXTRA_FILES rt.grd.nc sod.grd.nc +) diff --git a/examples/gyro-gem/CMakeLists.txt b/examples/gyro-gem/CMakeLists.txt index 7189bb06b8..5d83848688 100644 --- a/examples/gyro-gem/CMakeLists.txt +++ b/examples/gyro-gem/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(gyro-gem LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(gyro-gem +bout_add_example( + gyro-gem SOURCES gem.cxx - EXTRA_FILES cyclone_68x32.nc) + EXTRA_FILES cyclone_68x32.nc +) diff --git a/examples/gyro-gem/gem.cxx b/examples/gyro-gem/gem.cxx index 1a056c1c08..f4347ca2b3 100644 --- a/examples/gyro-gem/gem.cxx +++ b/examples/gyro-gem/gem.cxx @@ -4,17 +4,17 @@ * 6 moments for each species * * "GEM - An Energy Conserving Electromagnetic Gyrofluid Model" - * by Bruce D Scott. arXiv:physics/0501124v1 23 Jan 2005 + * by Bruce D Scott. arXiv:physics/0501124v1 23 Jan 2005 * * This version uses global parameters for collisionality etc. ****************************************************************/ +#include "bout/tokamak_coordinates.hxx" #include -#include - #include #include #include +#include /// Fundamental constants @@ -185,14 +185,6 @@ class GEM : public PhysicsModel { ////////////////////////////////// // Read profiles - // Mesh - Field2D Rxy, Bpxy, Btxy, Bxy, hthe; - GRID_LOAD(Rxy); // Major radius [m] - GRID_LOAD(Bpxy); // Poloidal B field [T] - GRID_LOAD(Btxy); // Toroidal B field [T] - GRID_LOAD(Bxy); // Total B field [T] - GRID_LOAD(hthe); // Poloidal arc length [m / radian] - GRID_LOAD(Te0); // Electron temperature in eV GRID_LOAD(Ni0); // Ion number density in 10^20 m^-3 @@ -255,6 +247,8 @@ class GEM : public PhysicsModel { if (mesh->get(Bbar, "Bbar")) { if (mesh->get(Bbar, "bmag")) { + Field3D Bxy; + mesh->get(Bxy, "Bxy"); Bbar = max(Bxy, true); } } @@ -355,44 +349,13 @@ class GEM : public PhysicsModel { ////////////////////////////////// // Metric tensor components - coord = mesh->getCoordinates(); - - // Normalise - hthe /= Lbar; // parallel derivatives normalised to Lperp - - Bpxy /= Bbar; - Btxy /= Bbar; - Bxy /= Bbar; - - Rxy /= rho_s; // Perpendicular derivatives normalised to rho_s - coord->dx /= rho_s * rho_s * Bbar; - - // Metric components - - coord->g11 = SQ(Rxy * Bpxy); - coord->g22 = 1.0 / SQ(hthe); - coord->g33 = SQ(Bxy) / coord->g11; - coord->g12 = 0.0; - coord->g13 = 0.; - coord->g23 = -Btxy / (hthe * Bpxy * Rxy); - - coord->J = hthe / Bpxy; - coord->Bxy = Bxy; - - coord->g_11 = 1.0 / coord->g11; - coord->g_22 = SQ(Bxy * hthe / Bpxy); - coord->g_33 = Rxy * Rxy; - coord->g_12 = 0.; - coord->g_13 = 0.; - coord->g_23 = Btxy * hthe * Rxy / Bpxy; - - coord->geometry(); + const auto tokamak_coords = bout::set_tokamak_coordinates(*mesh, rho_s, Bbar); // Set B field vector B0vec.covariant = false; B0vec.x = 0.; - B0vec.y = Bpxy / hthe; + B0vec.y = tokamak_coords.Bpxy / tokamak_coords.hthe; B0vec.z = 0.; // Precompute this for use in RHS diff --git a/examples/hasegawa-wakatani-3d/CMakeLists.txt b/examples/hasegawa-wakatani-3d/CMakeLists.txt index 0cdb5207f8..c555d6d7f9 100644 --- a/examples/hasegawa-wakatani-3d/CMakeLists.txt +++ b/examples/hasegawa-wakatani-3d/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(hw3d LANGUAGES CXX C) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/hasegawa-wakatani/CMakeLists.txt b/examples/hasegawa-wakatani/CMakeLists.txt index c9b9401b3a..53f4e5ed4f 100644 --- a/examples/hasegawa-wakatani/CMakeLists.txt +++ b/examples/hasegawa-wakatani/CMakeLists.txt @@ -2,9 +2,8 @@ cmake_minimum_required(VERSION 3.13) project(hasegawa-wakatani LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() bout_add_example(hasegawa-wakatani SOURCES hw.cxx) - diff --git a/examples/invertable_operator/CMakeLists.txt b/examples/invertable_operator/CMakeLists.txt index f054466f23..f18a085ec1 100644 --- a/examples/invertable_operator/CMakeLists.txt +++ b/examples/invertable_operator/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(invertable_operator LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(invertable_operator +bout_add_example( + invertable_operator SOURCES invertable_operator.cxx - REQUIRES BOUT_HAS_PETSC) + REQUIRES BOUT_HAS_PETSC +) diff --git a/examples/invertable_operator/invertable_operator.cxx b/examples/invertable_operator/invertable_operator.cxx index 57f9e24b08..99db7f7a1e 100644 --- a/examples/invertable_operator/invertable_operator.cxx +++ b/examples/invertable_operator/invertable_operator.cxx @@ -1,5 +1,4 @@ #include -#include #include #include @@ -26,7 +25,7 @@ class HW : public PhysicsModel { // Drop C term for now Field3D operator()(const Field3D& input) { - TRACE("myLaplacian::operator()"); + Timer timer("invertable_operator_operate"); Field3D result = A * input + D * Delp2(input); @@ -43,7 +42,7 @@ class HW : public PhysicsModel { // Drop C term for now Field3D operator()(const Field3D& input) { - TRACE("myLaplacian::operator()"); + Timer timer("invertable_operator_operate"); Field3D result = A * input + B * Laplace_perp(input); if (withDiv) { diff --git a/examples/laplace-petsc3d/plotcheck.py b/examples/laplace-petsc3d/plotcheck.py index 707e5db1ee..7f2758dd81 100755 --- a/examples/laplace-petsc3d/plotcheck.py +++ b/examples/laplace-petsc3d/plotcheck.py @@ -10,27 +10,27 @@ xg = 2 -f = collect('f', path=datadir)[xg:-xg, :, :] -rhs = collect('rhs', path=datadir)[xg:-xg, :, :] -rhs_check = collect('rhs_check', path=datadir)[xg:-xg, :, :] -error = collect('error', path=datadir)[xg:-xg, :, :] +f = collect("f", path=datadir)[xg:-xg, :, :] +rhs = collect("rhs", path=datadir)[xg:-xg, :, :] +rhs_check = collect("rhs_check", path=datadir)[xg:-xg, :, :] +error = collect("error", path=datadir)[xg:-xg, :, :] pyplot.subplot(221) pyplot.pcolormesh(f[:, yind, :]) pyplot.colorbar() -pyplot.title('f') +pyplot.title("f") pyplot.subplot(222) pyplot.pcolormesh(rhs[:, yind, :]) pyplot.colorbar() -pyplot.title('rhs') +pyplot.title("rhs") pyplot.subplot(223) pyplot.pcolormesh(rhs_check[:, yind, :]) pyplot.colorbar() -pyplot.title('rhs_check') +pyplot.title("rhs_check") pyplot.subplot(224) pyplot.pcolormesh(error[:, yind, :]) pyplot.colorbar() -pyplot.title('error') +pyplot.title("error") pyplot.show() diff --git a/examples/laplacexy/alfven-wave/CMakeLists.txt b/examples/laplacexy/alfven-wave/CMakeLists.txt index 2423400519..ebda7703e6 100644 --- a/examples/laplacexy/alfven-wave/CMakeLists.txt +++ b/examples/laplacexy/alfven-wave/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(laplacexy-alfven-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(laplacexy-alfven-wave +bout_add_example( + laplacexy-alfven-wave SOURCES alfven.cxx DATA_DIRS cbm18 data - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc d3d_119919.nc) + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc d3d_119919.nc +) diff --git a/examples/laplacexy/alfven-wave/alfven.cxx b/examples/laplacexy/alfven-wave/alfven.cxx index a4248afcf7..cf66af7295 100644 --- a/examples/laplacexy/alfven-wave/alfven.cxx +++ b/examples/laplacexy/alfven-wave/alfven.cxx @@ -1,9 +1,11 @@ - #include #include #include #include #include +#include + +#include /// Fundamental constants const BoutReal PI = 3.14159265; @@ -30,9 +32,9 @@ class Alfven : public PhysicsModel { BoutReal mu_epar; // Electron parallel viscosity BoutReal resistivity; - bool laplace_perp; // Use Laplace_perp or Delp2? - bool split_n0; // Split solve into n=0 and n~=0? - LaplaceXY* laplacexy; // Laplacian solver in X-Y (n=0) + bool laplace_perp; // Use Laplace_perp or Delp2? + bool split_n0; // Split solve into n=0 and n~=0? + std::unique_ptr laplacexy{nullptr}; // Laplacian solver in X-Y (n=0) bool newXZsolver; std::unique_ptr phiSolver; // Old Laplacian in X-Z @@ -69,7 +71,7 @@ class Alfven : public PhysicsModel { resistivity = opt["resistivity"].withDefault(1e-7); // Load metric tensor from the mesh, passing length and B field normalisations - LoadMetric(rho_s0, Bnorm); + bout::set_tokamak_coordinates(*mesh, rho_s0, Bnorm, true); // Specify evolving variables SOLVE_FOR2(Vort, Apar); @@ -80,7 +82,7 @@ class Alfven : public PhysicsModel { if (split_n0) { // Create an XY solver for n=0 component - laplacexy = new LaplaceXY(mesh); + laplacexy = LaplaceXY::create(mesh); phi2D = 0.0; // Starting guess } @@ -167,59 +169,6 @@ class Alfven : public PhysicsModel { return 0; } - - void LoadMetric(BoutReal Lnorm, BoutReal Bnorm) { - // Load metric coefficients from the mesh - Field2D Rxy, Bpxy, Btxy, hthe, sinty; - GRID_LOAD5(Rxy, Bpxy, Btxy, hthe, sinty); // Load metrics - - Coordinates* coord = mesh->getCoordinates(); // Metric tensor - - // Checking for dpsi and qinty used in BOUT grids - Field2D dx; - if (!mesh->get(dx, "dpsi")) { - output << "\tUsing dpsi as the x grid spacing\n"; - coord->dx = dx; // Only use dpsi if found - } else { - // dx will have been read already from the grid - output << "\tUsing dx as the x grid spacing\n"; - } - - Rxy /= Lnorm; - hthe /= Lnorm; - sinty *= SQ(Lnorm) * Bnorm; - coord->dx /= SQ(Lnorm) * Bnorm; - - Bpxy /= Bnorm; - Btxy /= Bnorm; - coord->Bxy /= Bnorm; - - // Calculate metric components - sinty = 0.0; // I disappears from metric for shifted coordinates - - BoutReal sbp = 1.0; // Sign of Bp - if (min(Bpxy, true) < 0.0) { - sbp = -1.0; - } - - coord->g11 = SQ(Rxy * Bpxy); - coord->g22 = 1.0 / SQ(hthe); - coord->g33 = SQ(sinty) * coord->g11 + SQ(coord->Bxy) / coord->g11; - coord->g12 = 0.0; - coord->g13 = -sinty * coord->g11; - coord->g23 = -sbp * Btxy / (hthe * Bpxy * Rxy); - - coord->J = hthe / Bpxy; - - coord->g_11 = 1.0 / coord->g11 + SQ(sinty * Rxy); - coord->g_22 = SQ(coord->Bxy * hthe / Bpxy); - coord->g_33 = Rxy * Rxy; - coord->g_12 = sbp * Btxy * hthe * sinty * Rxy / Bpxy; - coord->g_13 = sinty * Rxy * Rxy; - coord->g_23 = sbp * Btxy * hthe * Rxy / Bpxy; - - coord->geometry(); - } }; BOUTMAIN(Alfven); diff --git a/examples/laplacexy/laplace_perp/CMakeLists.txt b/examples/laplacexy/laplace_perp/CMakeLists.txt index 388513b044..d12725c5de 100644 --- a/examples/laplacexy/laplace_perp/CMakeLists.txt +++ b/examples/laplacexy/laplace_perp/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(laplacexy-laplace_perp LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(laplacexy-laplace_perp +bout_add_example( + laplacexy-laplace_perp SOURCES test.cxx - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc - DATA_DIRS square torus) + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc + DATA_DIRS square torus +) diff --git a/examples/laplacexy/laplace_perp/square/BOUT.inp b/examples/laplacexy/laplace_perp/square/BOUT.inp index 95ecb7119f..38fb175d90 100644 --- a/examples/laplacexy/laplace_perp/square/BOUT.inp +++ b/examples/laplacexy/laplace_perp/square/BOUT.inp @@ -1,5 +1,5 @@ -input = sin(pi*x - y) +input_field = sin(pi*x - y) non_uniform = true # Include corrections to second derivatives diff --git a/examples/laplacexy/laplace_perp/test.cxx b/examples/laplacexy/laplace_perp/test.cxx index 1312b005da..b1686d1764 100644 --- a/examples/laplacexy/laplace_perp/test.cxx +++ b/examples/laplacexy/laplace_perp/test.cxx @@ -1,8 +1,9 @@ #include - #include +#include #include #include +#include using bout::globals::mesh; @@ -10,47 +11,18 @@ int main(int argc, char** argv) { BoutInitialise(argc, argv); /////////////////////////////////////// - bool calc_metric; - calc_metric = Options::root()["calc_metric"].withDefault(false); + const bool calc_metric = Options::root()["calc_metric"].withDefault(false); if (calc_metric) { - // Read metric tensor - Field2D Rxy, Btxy, Bpxy, B0, hthe, I; - mesh->get(Rxy, "Rxy"); // m - mesh->get(Btxy, "Btxy"); // T - mesh->get(Bpxy, "Bpxy"); // T - mesh->get(B0, "Bxy"); // T - mesh->get(hthe, "hthe"); // m - mesh->get(I, "sinty"); // m^-2 T^-1 - - Coordinates* coord = mesh->getCoordinates(); - - // Calculate metrics - coord->g11 = SQ(Rxy * Bpxy); - coord->g22 = 1.0 / SQ(hthe); - coord->g33 = SQ(I) * coord->g11 + SQ(B0) / coord->g11; - coord->g12 = 0.0; - coord->g13 = -I * coord->g11; - coord->g23 = -Btxy / (hthe * Bpxy * Rxy); - - coord->J = hthe / Bpxy; - coord->Bxy = B0; - - coord->g_11 = 1.0 / coord->g11 + SQ(I * Rxy); - coord->g_22 = SQ(B0 * hthe / Bpxy); - coord->g_33 = Rxy * Rxy; - coord->g_12 = Btxy * hthe * I * Rxy / Bpxy; - coord->g_13 = I * Rxy * Rxy; - coord->g_23 = Btxy * hthe * Rxy / Bpxy; - - coord->geometry(); + bout::set_tokamak_coordinates(*mesh); } /////////////////////////////////////// // Read an analytic input - Field2D input = FieldFactory::get()->create2D("input", Options::getRoot(), mesh); + const Field2D input = + FieldFactory::get()->create2D("input_field", Options::getRoot(), mesh); // Create a LaplaceXY solver - LaplaceXY* laplacexy = new LaplaceXY(mesh); + auto laplacexy = LaplaceXY::create(mesh); // Solve, using 0.0 as starting guess Field2D solved = laplacexy->solve(input, 0.0); diff --git a/examples/laplacexy/laplace_perp/torus/BOUT.inp b/examples/laplacexy/laplace_perp/torus/BOUT.inp index 365294174f..4109b44bab 100644 --- a/examples/laplacexy/laplace_perp/torus/BOUT.inp +++ b/examples/laplacexy/laplace_perp/torus/BOUT.inp @@ -1,5 +1,5 @@ -input = sin(pi*x - y) +input_field = sin(pi*x - y) calc_metric = true # Read Rxy, Bpxy etc and calculate metric non_uniform = true # Include corrections to second derivatives diff --git a/examples/laplacexy/simple-hypre/.gitignore b/examples/laplacexy/simple-hypre/.gitignore deleted file mode 100644 index eeede075f9..0000000000 --- a/examples/laplacexy/simple-hypre/.gitignore +++ /dev/null @@ -1 +0,0 @@ -test-laplacexy diff --git a/examples/laplacexy/simple-hypre/CMakeLists.txt b/examples/laplacexy/simple-hypre/CMakeLists.txt deleted file mode 100644 index 788ebb1776..0000000000 --- a/examples/laplacexy/simple-hypre/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 3.13) - -project(test_laplacexy_hypre LANGUAGES CXX C) - -if (NOT TARGET bout++::bout++) - find_package(bout++ REQUIRED) -endif() - -bout_add_example(test_laplacexy_hypre - SOURCES test-laplacexy-hypre.cxx - REQUIRES BOUT_HAS_HYPRE) - -if(BOUT_HAS_CUDA) - set_source_files_properties(test-laplacexy-hypre.cxx PROPERTIES LANGUAGE CUDA ) -endif() diff --git a/examples/laplacexy/simple-hypre/data/BOUT.inp b/examples/laplacexy/simple-hypre/data/BOUT.inp deleted file mode 100644 index 5da492168d..0000000000 --- a/examples/laplacexy/simple-hypre/data/BOUT.inp +++ /dev/null @@ -1,37 +0,0 @@ -# -# Simple test of the LaplaceXY solver. -# -# Inverts a given function (rhs), writing "rhs" and solution "x" to file -# - -[mesh] - -# Mesh sizes -nx = 20 -ny = 32 -nz = 1 - -# mesh spacing -dx = 1.0 -dy = 1.0 -dz = 1.0 - -[laplacexy] - -ksptype = gmres # Iterative solver type - not used with Hypre interface -pctype = jacobi # Preconditioner. "jacobi", "bjacobi" and "sor" usually good -# On one processor"lu" uses direct solver - -atol = 1e-12 # type: BoutReal, doc: Relative tolerance for Hypre solver -core_bndry_dirichlet = false # type: bool -hypre_print_level = 3 # type: int, doc: Verbosity for Hypre solver. Integer from 0 (silent) to 4 (most verbose). -#hypre_solver_type = gmres # type: HYPRE_SOLVER_TYPE, doc: Type of solver to use when solving Hypre system. Possible values are: gmres, bicgstab -hypre_solver_type = bicgstab # type: HYPRE_SOLVER_TYPE, doc: Type of solver to use when solving Hypre system. Possible values are: gmres, bicgstab -include_y_derivs = true # type: bool, doc: Include Y derivatives in operator to invert? -maxits = 2000 # type: int, doc: Maximum iterations for Hypre solver -print_timing = true # type: bool, doc: Print extra timing information for LaplaceXY2Hypre -rtol = 1e-07 # type: BoutReal, doc: Relative tolerance for Hypre solver -y_bndry_dirichlet = false # type: bool - -# Function to be inverted -rhs = sin(2*pi*x)*sin(y) diff --git a/examples/laplacexy/simple-hypre/test-laplacexy-hypre.cxx b/examples/laplacexy/simple-hypre/test-laplacexy-hypre.cxx deleted file mode 100644 index af2367e1a0..0000000000 --- a/examples/laplacexy/simple-hypre/test-laplacexy-hypre.cxx +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include - -int main(int argc, char** argv) { - BoutInitialise(argc, argv); - { - /// Create a LaplaceXY object - LaplaceXY2Hypre laplacexy(bout::globals::mesh); - - /// Generate rhs function - Field2D rhs = FieldFactory::get()->create2D("laplacexy:rhs", Options::getRoot(), - bout::globals::mesh); - - /// Solution - Field2D solution = 0.0; - - solution = laplacexy.solve(rhs, solution); - - Options dump; - dump["rhs"] = rhs; - dump["x"] = solution; - bout::writeDefaultOutputFile(dump); - } - BoutFinalise(); -#if BOUT_HAS_CUDA - cudaDeviceReset(); -#endif - return 0; -} diff --git a/examples/laplacexy/simple/CMakeLists.txt b/examples/laplacexy/simple/CMakeLists.txt index ec5e7d2492..e999b9cf58 100644 --- a/examples/laplacexy/simple/CMakeLists.txt +++ b/examples/laplacexy/simple/CMakeLists.txt @@ -2,8 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(laplacexy-simple LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(laplacexy-simple SOURCES test-laplacexy.cxx) +bout_add_example( + laplacexy-simple + SOURCES test-laplacexy.cxx + DATA_DIRS data hypre +) diff --git a/examples/laplacexy/simple/README.md b/examples/laplacexy/simple/README.md index ce7cbebf00..491d00d4b3 100644 --- a/examples/laplacexy/simple/README.md +++ b/examples/laplacexy/simple/README.md @@ -8,7 +8,7 @@ and preconditioners. See the "ksptype" and "pctype" settings in BOUT.inp Run with - $ ./test-laplacexy -ksp_monitor + $ ./test-laplacexy -laplacexy:petsc:ksp_monitor which should print the KSP norms from PETSc: @@ -31,3 +31,8 @@ which should print the KSP norms from PETSc: 16 KSP Residual norm 4.309526296050e-01 17 KSP Residual norm 1.115269396077e-01 18 KSP Residual norm 4.334487475743e-13 + +HYPRE +----- + +Use the `hypre` directory to use the HYPRE preconditioner instead of PETSc. diff --git a/examples/laplacexy/simple/hypre/BOUT.inp b/examples/laplacexy/simple/hypre/BOUT.inp new file mode 100644 index 0000000000..338e902d4e --- /dev/null +++ b/examples/laplacexy/simple/hypre/BOUT.inp @@ -0,0 +1,23 @@ +# +# Simple test of the LaplaceXY solver using HYPRE +# +# Inverts a given function (rhs), writing "rhs" and solution "x" to file +# + +[mesh] + +# Mesh sizes +nx = 20 +ny = 32 +nz = 1 + +# mesh spacing +dx = 1.0 +dy = 1.0 +dz = 1.0 + +[laplacexy] +type = hypre + +# Function to be inverted +rhs = sin(2*pi*x)*sin(y) diff --git a/examples/laplacexy/simple/test-laplacexy.cxx b/examples/laplacexy/simple/test-laplacexy.cxx index c033eb68e3..49a93aafdb 100644 --- a/examples/laplacexy/simple/test-laplacexy.cxx +++ b/examples/laplacexy/simple/test-laplacexy.cxx @@ -7,20 +7,18 @@ int main(int argc, char** argv) { BoutInitialise(argc, argv); /// Create a LaplaceXY object - LaplaceXY laplacexy(bout::globals::mesh); + auto laplacexy = LaplaceXY::create(bout::globals::mesh); /// Generate rhs function Field2D rhs = FieldFactory::get()->create2D("laplacexy:rhs", Options::getRoot(), bout::globals::mesh); /// Solution - Field2D x = 0.0; - - x = laplacexy.solve(rhs, x); + Field2D result = laplacexy->solve(rhs, 0.0); Options dump; dump["rhs"] = rhs; - dump["x"] = x; + dump["result"] = result; bout::writeDefaultOutputFile(dump); BoutFinalise(); diff --git a/examples/monitor-newapi/CMakeLists.txt b/examples/monitor-newapi/CMakeLists.txt index 0ee3ee7f85..5c2022b792 100644 --- a/examples/monitor-newapi/CMakeLists.txt +++ b/examples/monitor-newapi/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(monitor-newapi LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/orszag-tang/CMakeLists.txt b/examples/orszag-tang/CMakeLists.txt index 9ac8fd8d1c..46eae5d0f2 100644 --- a/examples/orszag-tang/CMakeLists.txt +++ b/examples/orszag-tang/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(orszag-tang LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(orszag-tang +bout_add_example( + orszag-tang SOURCES mhd.cxx - EXTRA_FILES data/otv.grd.nc) + EXTRA_FILES data/otv.grd.nc +) diff --git a/examples/orszag-tang/generate.py b/examples/orszag-tang/generate.py index 3ba6dbbea9..acce7497c3 100644 --- a/examples/orszag-tang/generate.py +++ b/examples/orszag-tang/generate.py @@ -2,14 +2,16 @@ from __future__ import division from builtins import range from past.utils import old_div + # the Scientific Python netCDF 3 interface # http://dirac.cnrs-orleans.fr/ScientificPython/ -#from Scientific.IO.NetCDF import NetCDFFile as Dataset +# from Scientific.IO.NetCDF import NetCDFFile as Dataset # the 'classic' version of the netCDF4 python interface # http://code.google.com/p/netcdf4-python/ import numpy as np from netCDF4 import Dataset -from numpy import dtype # array module from http://numpy.scipy.org +from numpy import dtype # array module from http://numpy.scipy.org + """ This example writes some surface pressure and temperatures The companion program sfc_pres_temp_rd.py shows how to read the netCDF @@ -30,44 +32,45 @@ # # the output array to write will be nx x ny -ny = 100; nx = ny + 4 +ny = 100 +nx = ny + 4 # dy of grid dy = old_div(1.0, np.float(ny)) dx = dy # create grid -dxarr=np.zeros((nx,ny),dtype='float32')+dx -dyarr=np.zeros((nx,ny),dtype='float32')+dy +dxarr = np.zeros((nx, ny), dtype="float32") + dx +dyarr = np.zeros((nx, ny), dtype="float32") + dy -xarr=np.arange(0.,np.float(nx),1.,dtype='float32')*dx -yarr=np.arange(0.,np.float(ny),1.,dtype='float32')*dy +xarr = np.arange(0.0, np.float(nx), 1.0, dtype="float32") * dx +yarr = np.arange(0.0, np.float(ny), 1.0, dtype="float32") * dy # compute initial variables -rho=np.zeros((nx,ny),dtype='float32')+old_div(25.,(36.*np.pi)) -p=np.zeros((nx,ny),dtype='float32')+old_div(5.,(12.*np.pi)) +rho = np.zeros((nx, ny), dtype="float32") + old_div(25.0, (36.0 * np.pi)) +p = np.zeros((nx, ny), dtype="float32") + old_div(5.0, (12.0 * np.pi)) -rho=1. -p=old_div(rho,3.) +rho = 1.0 +p = old_div(rho, 3.0) -v_x=np.zeros((nx,ny),dtype='float32') -Bx=np.zeros((nx,ny),dtype='float32') +v_x = np.zeros((nx, ny), dtype="float32") +Bx = np.zeros((nx, ny), dtype="float32") for y in range(ny): - v_x[:,y]=-np.sin(2.*np.pi*yarr[y]) - Bx[:,y]=-np.sin(2.*np.pi*yarr[y]) - -#Bx=Bx/np.sqrt(4.*np.pi) + v_x[:, y] = -np.sin(2.0 * np.pi * yarr[y]) + Bx[:, y] = -np.sin(2.0 * np.pi * yarr[y]) +# Bx=Bx/np.sqrt(4.*np.pi) -v_y=np.zeros((nx,ny),dtype='float32') -By=np.zeros((nx,ny),dtype='float32') + +v_y = np.zeros((nx, ny), dtype="float32") +By = np.zeros((nx, ny), dtype="float32") for x in range(nx): - v_y[x,:]=np.sin(2.*np.pi*xarr[x]) - By[x,:]=np.sin(4.*np.pi*xarr[x]) - -#By=By/np.sqrt(4.*np.pi) + v_y[x, :] = np.sin(2.0 * np.pi * xarr[x]) + By[x, :] = np.sin(4.0 * np.pi * xarr[x]) + +# By=By/np.sqrt(4.*np.pi) # Domain inside core (periodic) @@ -76,55 +79,62 @@ ixseps2 = nx # open a new netCDF file for writing. -ncfile = Dataset('otv.grd.128.nc','w', format='NETCDF3_CLASSIC') +ncfile = Dataset("otv.grd.128.nc", "w", format="NETCDF3_CLASSIC") # output data. # create the nx and ny dimensions. -ncfile.createDimension('x',nx) -ncfile.createDimension('y',ny) -ncfile.createDimension('single',1) +ncfile.createDimension("x", nx) +ncfile.createDimension("y", ny) +ncfile.createDimension("single", 1) # create and write nx,ny variables -nxx=ncfile.createVariable('nx','i4',('single')) -nyy=ncfile.createVariable('ny','i4',('single')) +nxx = ncfile.createVariable("nx", "i4", ("single")) +nyy = ncfile.createVariable("ny", "i4", ("single")) -nxx[:]=nx -nyy[:]=ny +nxx[:] = nx +nyy[:] = ny # Define the coordinate variables. They will hold the coordinate # information, that is, xarr,yarr -dx = ncfile.createVariable('dx',dtype('float32').char,('x','y')) -dy = ncfile.createVariable('dy',dtype('float32').char,('x','y',)) +dx = ncfile.createVariable("dx", dtype("float32").char, ("x", "y")) +dy = ncfile.createVariable( + "dy", + dtype("float32").char, + ( + "x", + "y", + ), +) # write data to coordinate vars. -dx[:,:] = dxarr -dy[:,:] = dyarr +dx[:, :] = dxarr +dy[:, :] = dyarr # create and write ixseps* dimensions. -ix1=ncfile.createVariable('ixseps1','i4',('single')) -ix2=ncfile.createVariable('ixseps2','i4',('single')) +ix1 = ncfile.createVariable("ixseps1", "i4", ("single")) +ix2 = ncfile.createVariable("ixseps2", "i4", ("single")) -ix1[:]=ixseps1 -ix2[:]=ixseps2 +ix1[:] = ixseps1 +ix2[:] = ixseps2 -# create the corresponding variables -rho0 = ncfile.createVariable('rho0',dtype('float32').char,('x','y')) -p0 = ncfile.createVariable('p0',dtype('float32').char,('x','y')) -v0_x = ncfile.createVariable('v0_x',dtype('float32').char,('x','y')) -v0_y = ncfile.createVariable('v0_y',dtype('float32').char,('x','y')) -B0x = ncfile.createVariable('B0x',dtype('float32').char,('x','y')) -B0y = ncfile.createVariable('B0y',dtype('float32').char,('x','y')) +# create the corresponding variables +rho0 = ncfile.createVariable("rho0", dtype("float32").char, ("x", "y")) +p0 = ncfile.createVariable("p0", dtype("float32").char, ("x", "y")) +v0_x = ncfile.createVariable("v0_x", dtype("float32").char, ("x", "y")) +v0_y = ncfile.createVariable("v0_y", dtype("float32").char, ("x", "y")) +B0x = ncfile.createVariable("B0x", dtype("float32").char, ("x", "y")) +B0y = ncfile.createVariable("B0y", dtype("float32").char, ("x", "y")) # write data to variables. -rho0[:,:]=rho -p0[:,:]=p -v0_x[:,:]=v_x -v0_y[:,:]=v_y -B0x[:,:]=Bx -B0y[:,:]=By +rho0[:, :] = rho +p0[:, :] = p +v0_x[:, :] = v_x +v0_y[:, :] = v_y +B0x[:, :] = Bx +B0y[:, :] = By ncfile.close() -print('*** SUCCESS writing file otv.grd.py.nc!') +print("*** SUCCESS writing file otv.grd.py.nc!") diff --git a/examples/performance/bracket/bracket.cxx b/examples/performance/bracket/bracket.cxx index 50012510ff..0ef9e241fb 100644 --- a/examples/performance/bracket/bracket.cxx +++ b/examples/performance/bracket/bracket.cxx @@ -68,9 +68,6 @@ int main(int argc, char** argv) { ITERATOR_TEST_BLOCK("Bracket [2D,3D] ARAKAWA", result = bracket(a, c, BRACKET_ARAKAWA);); - ITERATOR_TEST_BLOCK("Bracket [2D,3D] ARAKAWA_OLD", - result = bracket(a, c, BRACKET_ARAKAWA_OLD);); - ITERATOR_TEST_BLOCK("Bracket [2D,3D] SIMPLE", result = bracket(a, c, BRACKET_SIMPLE);); @@ -81,21 +78,12 @@ int main(int argc, char** argv) { ITERATOR_TEST_BLOCK("Bracket [3D,3D] ARAKAWA", result = bracket(a, b, BRACKET_ARAKAWA);); - ITERATOR_TEST_BLOCK("Bracket [3D,3D] ARAKAWA_OLD", - result = bracket(a, b, BRACKET_ARAKAWA_OLD);); - ITERATOR_TEST_BLOCK("Bracket [3D,3D] SIMPLE", result = bracket(a, b, BRACKET_SIMPLE);); ITERATOR_TEST_BLOCK("Bracket [3D,3D] DEFAULT", result = bracket(a, b, BRACKET_STD);); } - // Uncomment below for a "correctness" check - // Field3D resNew = bracket(a, b, BRACKET_ARAKAWA); mesh->communicate(resNew); - // Field3D resOld = bracket(a, b, BRACKET_ARAKAWA_OLD); mesh->communicate(resOld); - // time_output << "Max abs diff is - // "<LocalNx; ++i) { for (int j = mesh->ystart; j < mesh->yend; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { result(i, j, k) = (a(i, j + 1, k) - a(i, j - 1, k)); } } @@ -76,7 +76,7 @@ int main(int argc, char** argv) { BOUT_OMP_PERF(parallel for) for(int i=0;iLocalNx;++i) { for (int j = mesh->ystart; j < mesh->yend; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { result(i, j, k) = (a(i, j + 1, k) - a(i, j - 1, k)); } } diff --git a/examples/performance/iterator/iterator.cxx b/examples/performance/iterator/iterator.cxx index 7a29b00298..e1fc59d067 100644 --- a/examples/performance/iterator/iterator.cxx +++ b/examples/performance/iterator/iterator.cxx @@ -77,7 +77,7 @@ int main(int argc, char** argv) { ITERATOR_TEST_BLOCK( "Nested loop", for (int i = 0; i < mesh->LocalNx; ++i) { for (int j = 0; j < mesh->LocalNy; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { result(i, j, k) = a(i, j, k) + b(i, j, k); } } @@ -88,7 +88,7 @@ int main(int argc, char** argv) { BOUT_OMP_PERF(parallel for) for(int i=0;iLocalNx;++i) { for (int j = 0; j < mesh->LocalNy; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { result(i, j, k) = a(i, j, k) + b(i, j, k); } } diff --git a/examples/performance/iterator/scaling_parser.py b/examples/performance/iterator/scaling_parser.py index d7a23842d4..15f0ea8780 100644 --- a/examples/performance/iterator/scaling_parser.py +++ b/examples/performance/iterator/scaling_parser.py @@ -2,8 +2,7 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): @@ -13,7 +12,7 @@ def read_file(filename): for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} diff --git a/examples/preconditioning/wave/CMakeLists.txt b/examples/preconditioning/wave/CMakeLists.txt index 437f39fe3a..05d7548832 100644 --- a/examples/preconditioning/wave/CMakeLists.txt +++ b/examples/preconditioning/wave/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(preconditioning-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/preconditioning/wave/data/BOUT.inp b/examples/preconditioning/wave/data/BOUT.inp index b7a2e816c3..ca2f31c741 100644 --- a/examples/preconditioning/wave/data/BOUT.inp +++ b/examples/preconditioning/wave/data/BOUT.inp @@ -12,7 +12,7 @@ first = c2 [solver] type = cvode # Need CVODE or PETSc -use_precon = true # Use preconditioner +cvode_precon_method = user # Use user-supplied preconditioner use_jacobian = false # Use user-supplied preconditioner? rightprec = false # Use Right preconditioner (default left) diagnose = true # Print additional diagnostics diff --git a/examples/staggered_grid/CMakeLists.txt b/examples/staggered_grid/CMakeLists.txt index dd2b3b463e..1950d16c0b 100644 --- a/examples/staggered_grid/CMakeLists.txt +++ b/examples/staggered_grid/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(staggered_grid LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(staggered_grid +bout_add_example( + staggered_grid SOURCES test_staggered.cxx EXTRA_FILES generate.py run test-staggered.nc - DATA_DIRS data test) + DATA_DIRS data test +) diff --git a/examples/staggered_grid/generate.py b/examples/staggered_grid/generate.py index da84e9c2e6..a5a0120612 100755 --- a/examples/staggered_grid/generate.py +++ b/examples/staggered_grid/generate.py @@ -4,11 +4,11 @@ # Generate an input mesh # -from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries +from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries -nx = 5 # Minimum is 5: 2 boundary, one evolved +nx = 5 # Minimum is 5: 2 boundary, one evolved ny = 32 # Minimum 5. Should be divisible by number of processors (so powers of 2 nice) -dy = 1. # distance between points in y, in m/g22/lengthunit +dy = 1.0 # distance between points in y, in m/g22/lengthunit ixseps1 = -1 ixseps2 = -1 diff --git a/examples/subsampling/CMakeLists.txt b/examples/subsampling/CMakeLists.txt index 86f71d98f5..c6c487e687 100644 --- a/examples/subsampling/CMakeLists.txt +++ b/examples/subsampling/CMakeLists.txt @@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(subsampling LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(subsampling +bout_add_example( + subsampling SOURCES monitor.cxx - EXTRA_FILES show.py) - + EXTRA_FILES show.py +) diff --git a/examples/subsampling/show.py b/examples/subsampling/show.py index 34c6ee3084..b79c5e2964 100755 --- a/examples/subsampling/show.py +++ b/examples/subsampling/show.py @@ -11,14 +11,14 @@ for pack in monitors: filename, data_name = pack - t = DataFile(path+'/'+filename+'.dmp.0.nc').read('t_array') - data = DataFile(path+'/'+filename+'.dmp.0.nc').read(data_name).flatten() + t = DataFile(path + "/" + filename + ".dmp.0.nc").read("t_array") + data = DataFile(path + "/" + filename + ".dmp.0.nc").read(data_name).flatten() plt.plot(t, data, label="{} {}".format(filename, data_name)) -time = DataFile(path+'/BOUT.dmp.0.nc').read('t_array') -data = DataFile(path+'/BOUT.dmp.0.nc').read("T")[:, 2, 2, 0] +time = DataFile(path + "/BOUT.dmp.0.nc").read("t_array") +data = DataFile(path + "/BOUT.dmp.0.nc").read("T")[:, 2, 2, 0] -plt.plot(time, data, marker='+', label="BOUT++ T") +plt.plot(time, data, marker="+", label="BOUT++ T") plt.xlabel("Time") plt.legend() diff --git a/examples/wave-slab/CMakeLists.txt b/examples/wave-slab/CMakeLists.txt index b1943c4e1c..8de548b71d 100644 --- a/examples/wave-slab/CMakeLists.txt +++ b/examples/wave-slab/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(wave-slab LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(wave-slab +bout_add_example( + wave-slab SOURCES wave_slab.cxx - EXTRA_FILES generate.py) + EXTRA_FILES generate.py +) diff --git a/examples/wave-slab/generate.py b/examples/wave-slab/generate.py index 0a2e8f3049..12698d1e24 100755 --- a/examples/wave-slab/generate.py +++ b/examples/wave-slab/generate.py @@ -51,7 +51,7 @@ for x in range(nx): Bpxy[x, :] = Bpx[x] -Bxy = sqrt(Bpxy ** 2 + Bt ** 2) +Bxy = sqrt(Bpxy**2 + Bt**2) # Calculate change in poloidal flux dr = Lx / nx # Constant mesh spacing in radius diff --git a/examples/wave-slab/wave_slab.cxx b/examples/wave-slab/wave_slab.cxx index 4169b63dab..6101268e72 100644 --- a/examples/wave-slab/wave_slab.cxx +++ b/examples/wave-slab/wave_slab.cxx @@ -1,54 +1,22 @@ /* Simple wave test in a sheared slab domain. - + Uses the same field-aligned Clebsch coordinate system as most BOUT++ tokamak simulations. See the coordinates manual for details. - - Note: Here the only components of the coordinate system which + + Note: Here the only components of the coordinate system which are tested are g_22 (for Grad_par), and the twist shift angle. - + */ +#include "bout/tokamak_coordinates.hxx" #include class WaveTest : public PhysicsModel { public: int init(bool UNUSED(restarting)) { - auto* coords = mesh->getCoordinates(); - Field2D Rxy, Bpxy, Btxy, hthe, I; - GRID_LOAD(Rxy); - GRID_LOAD(Bpxy); - GRID_LOAD(Btxy); - GRID_LOAD(hthe); - mesh->get(coords->Bxy, "Bxy"); - int ShiftXderivs = 0; - mesh->get(ShiftXderivs, "false"); - if (ShiftXderivs) { - // No integrated shear in metric - I = 0.0; - } else { - mesh->get(I, "sinty"); - } - - coords->g11 = pow(Rxy * Bpxy, 2.0); - coords->g22 = 1.0 / pow(hthe, 2.0); - coords->g33 = pow(I, 2.0) * coords->g11 + pow(coords->Bxy, 2.0) / coords->g11; - coords->g12 = 0.0; - coords->g13 = -I * coords->g11; - coords->g23 = -Btxy / (hthe * Bpxy * Rxy); - - coords->J = hthe / Bpxy; - - coords->g_11 = 1.0 / coords->g11 + (pow(I * Rxy, 2.0)); - coords->g_22 = pow(coords->Bxy * hthe / Bpxy, 2.0); - coords->g_33 = Rxy * Rxy; - coords->g_12 = Btxy * hthe * I * Rxy / Bpxy; - coords->g_13 = I * Rxy * Rxy; - coords->g_23 = Btxy * hthe * Rxy / Bpxy; - - coords->geometry(); - + bout::set_tokamak_coordinates(*mesh, 1.0, 1.0, true); solver->add(f, "f"); solver->add(g, "g"); diff --git a/externalpackages/PVODE/CMakeLists.txt b/externalpackages/PVODE/CMakeLists.txt index 5e3f8f2f63..51afe8047c 100644 --- a/externalpackages/PVODE/CMakeLists.txt +++ b/externalpackages/PVODE/CMakeLists.txt @@ -6,14 +6,17 @@ else() cmake_policy(VERSION 3.12) endif() -project(PVODE +project( + PVODE DESCRIPTION "ODE Solver" VERSION 0.1 - LANGUAGES CXX) + LANGUAGES CXX +) find_package(MPI REQUIRED) -add_library(pvode +add_library( + pvode source/cvode.cpp source/nvector.cpp source/llnlmath.cpp @@ -33,45 +36,43 @@ add_library(pvode include/pvode/smalldense.h include/pvode/spgmr.h include/pvode/vector.h - ) - -target_include_directories(pvode PUBLIC - $ - $ - $ - ) +) + +target_include_directories( + pvode + PUBLIC $ + $ + $ +) target_link_libraries(pvode PUBLIC MPI::MPI_CXX) -add_library(pvpre - include/pvode/pvbbdpre.h - precon/pvbbdpre.cpp - precon/band.cpp - precon/band.h - ) - +add_library( + pvpre include/pvode/pvbbdpre.h precon/pvbbdpre.cpp precon/band.cpp + precon/band.h +) -set_target_properties(pvode PROPERTIES - SOVERSION 1.0.0) +set_target_properties(pvode PROPERTIES SOVERSION 1.0.0) -target_include_directories(pvpre PUBLIC - $ - $ - $ - ) +target_include_directories( + pvpre + PUBLIC $ + $ + $ +) target_link_libraries(pvpre PUBLIC pvode MPI::MPI_CXX) - -set_target_properties(pvpre PROPERTIES - SOVERSION 1.0.0) +set_target_properties(pvpre PROPERTIES SOVERSION 1.0.0) include(GNUInstallDirs) -install(TARGETS pvode pvpre +install( + TARGETS pvode pvpre EXPORT PVODETargets LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - ) + INCLUDES + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) include(CMakePackageConfigHelpers) @@ -79,17 +80,19 @@ write_basic_package_version_file( PVODEConfigVersion.cmake VERSION ${PACKAGE_VERSION} COMPATIBILITY SameMajorVersion - ) +) -install(EXPORT PVODETargets +install( + EXPORT PVODETargets FILE PVODEConfig.cmake NAMESPACE PVODE:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/PVODE" - ) +) -export(EXPORT PVODETargets +export( + EXPORT PVODETargets FILE "${CMAKE_CURRENT_BINARY_DIR}/PVODEConfig.cmake" NAMESPACE PVODE:: - ) +) export(PACKAGE PVODE) diff --git a/externalpackages/PVODE/makefile b/externalpackages/PVODE/makefile deleted file mode 100644 index 410470a320..0000000000 --- a/externalpackages/PVODE/makefile +++ /dev/null @@ -1,16 +0,0 @@ -# Makefile for example codes -# Works in the same way as the root Makefile - -TARGET?=all - -DIRS = source precon - -all: $(DIRS) - -.PHONY: $(DIRS) - -$(DIRS): - @$(MAKE) -s --no-print-directory TARGET=$(TARGET) -C $@ $(TARGET) - -clean:: - @for pp in $(DIRS); do echo " " $$pp cleaned; $(MAKE) --no-print-directory -C $$pp clean; done diff --git a/externalpackages/boutdata b/externalpackages/boutdata index e458cf0cf2..11f5d0acb3 160000 --- a/externalpackages/boutdata +++ b/externalpackages/boutdata @@ -1 +1 @@ -Subproject commit e458cf0cf2af6ff68db91da39ef3e15a7e9e6b3d +Subproject commit 11f5d0acb3d92a30eb0f97866c206a238c487076 diff --git a/externalpackages/cpptrace b/externalpackages/cpptrace new file mode 160000 index 0000000000..027f9aee2d --- /dev/null +++ b/externalpackages/cpptrace @@ -0,0 +1 @@ +Subproject commit 027f9aee2d34dbe1c98f26224e1fbe1654cb4aae diff --git a/externalpackages/fmt b/externalpackages/fmt index 2ac6c5ca8b..407c905e45 160000 --- a/externalpackages/fmt +++ b/externalpackages/fmt @@ -1 +1 @@ -Subproject commit 2ac6c5ca8b3dfbcb1cc5cf49a8cc121e3984559c +Subproject commit 407c905e45ad75fc29bf0f9bb7c5c2fd3475976f diff --git a/externalpackages/googletest b/externalpackages/googletest index 0953a17a42..8b53336594 160000 --- a/externalpackages/googletest +++ b/externalpackages/googletest @@ -1 +1 @@ -Subproject commit 0953a17a4281fc26831da647ad3fcd5e21e6473b +Subproject commit 8b53336594cc52213c6c2c7a0b29194fa896d039 diff --git a/include/boundary_factory.hxx b/include/boundary_factory.hxx deleted file mode 100644 index abbead8fc4..0000000000 --- a/include/boundary_factory.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "boundary_factory.hxx" has moved to "bout/boundary_factory.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/boundary_factory.hxx" diff --git a/include/boundary_op.hxx b/include/boundary_op.hxx deleted file mode 100644 index c96310f2d4..0000000000 --- a/include/boundary_op.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "boundary_op.hxx" has moved to "bout/boundary_op.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/boundary_op.hxx" diff --git a/include/boundary_region.hxx b/include/boundary_region.hxx deleted file mode 100644 index f487e88aeb..0000000000 --- a/include/boundary_region.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "boundary_region.hxx" has moved to "bout/boundary_region.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/boundary_region.hxx" diff --git a/include/boundary_standard.hxx b/include/boundary_standard.hxx deleted file mode 100644 index b7f17cf363..0000000000 --- a/include/boundary_standard.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "boundary_standard.hxx" has moved to "bout/boundary_standard.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/boundary_standard.hxx" diff --git a/include/bout.hxx b/include/bout.hxx deleted file mode 100644 index 926f035d2f..0000000000 --- a/include/bout.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "bout.hxx" has moved to "bout/bout.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/bout.hxx" diff --git a/include/bout/adios_object.hxx b/include/bout/adios_object.hxx index 683592e09b..63a2d4f85f 100755 --- a/include/bout/adios_object.hxx +++ b/include/bout/adios_object.hxx @@ -16,6 +16,8 @@ #if BOUT_HAS_ADIOS2 +#include "bout/boutexception.hxx" + #include #include #include @@ -36,14 +38,12 @@ IOPtr GetIOPtr(const std::string IOName); class ADIOSStream { public: adios2::IO io; - adios2::Engine engine; adios2::Variable vTime; adios2::Variable vStep; int adiosStep = 0; - bool isInStep = false; // true if BeginStep was called and EndStep was not yet called /** create or return the ADIOSStream based on the target file name */ - static ADIOSStream& ADIOSGetStream(const std::string& fname); + static ADIOSStream& ADIOSGetStream(const std::string& fname, adios2::Mode mode); ~ADIOSStream(); @@ -57,25 +57,80 @@ public: } template - adios2::Variable GetArrayVariable(const std::string& varname, adios2::Dims& shape) { + adios2::Variable + GetArrayVariable(const std::string& varname, const adios2::Dims& shape, + const std::vector& dimNames, int rank) { adios2::Variable v = io.InquireVariable(varname); if (!v) { adios2::Dims start(shape.size()); v = io.DefineVariable(varname, shape, start, shape); + if (!rank && dimNames.size()) { + io.DefineAttribute("__xarray_dimensions__", dimNames.data(), + dimNames.size(), varname, "/", true); + } } else { v.SetShape(shape); } return v; } + auto engine() -> adios2::Engine& { + if (not engine_) { + engine_ = io.Open(fname, file_mode); + if (not engine_) { + throw BoutException("Could not open ADIOS file '{:s}' for writing", fname); + } + } + return engine_; + } + + void beginStep() { + if (not isInStep) { + engine().BeginStep(); + isInStep = true; + adiosStep = static_cast(engine().CurrentStep()); + } + } + + void endStep() { + if (isInStep) { + engine().EndStep(); + isInStep = false; + } + } + + void finish() { + if (engine_) { + engine().EndStep(); + engine().Close(); + } + } + private: - ADIOSStream(const std::string fname) : fname(fname){}; + ADIOSStream(const std::string& fname, adios2::Mode mode) + : fname(fname), file_mode(mode) { + + ADIOSPtr adiosp = GetADIOSPtr(); + std::string ioname = "write_" + fname; + try { + io = adiosp->AtIO(ioname); + } catch (const std::invalid_argument& e) { + io = adiosp->DeclareIO(ioname); + io.SetEngine("BP5"); + } + }; + std::string fname; + adios2::Mode file_mode; + adios2::Engine engine_; + + /// true if BeginStep was called and EndStep was not yet called + bool isInStep = false; }; /** Set user parameters for an IO group */ -void ADIOSSetParameters(const std::string& input, const char delimKeyValue, - const char delimItem, adios2::IO& io); +void ADIOSSetParameters(const std::string& input, char delimKeyValue, char delimItem, + adios2::IO& io); } // namespace bout diff --git a/include/bout/array.hxx b/include/bout/array.hxx index 2c42f15aad..bb98ff6b0b 100644 --- a/include/bout/array.hxx +++ b/include/bout/array.hxx @@ -29,6 +29,7 @@ #include #include #include +#include #include #if BOUT_USE_OPENMP @@ -67,6 +68,7 @@ struct ArrayData { auto& rm = umpire::ResourceManager::getInstance(); #if BOUT_HAS_CUDA auto allocator = rm.getAllocator(umpire::resource::Pinned); + //auto allocator = rm.getAllocator(umpire::resource::Unified); #else auto allocator = rm.getAllocator("HOST"); #endif @@ -226,6 +228,12 @@ public: ptr = get(new_size); } + /*! + * Change shape of the container. + * Invalidates contents. + */ + void reshape(std::tuple new_shape) { reallocate(std::get<0>(new_shape)); } + /*! * Holds a static variable which controls whether * memory blocks (dataBlock) are put into a store @@ -283,6 +291,9 @@ public: return ptr->size(); } + /// Return shape of the array (the `size()` in a length-1 tuple) + std::tuple shape() const { return std::make_tuple(size()); }; + /*! * Returns true if the data is unique to this Array. * diff --git a/include/bout/assert.hxx b/include/bout/assert.hxx index af7caf4d9d..63bed2407b 100644 --- a/include/bout/assert.hxx +++ b/include/bout/assert.hxx @@ -2,16 +2,16 @@ * Defines a macro ASSERT which throws a BoutException if a given * condition is false. Whether the assertion is tested depends on * the checking level, so assetions can be removed for optimised runs. - * + * * ASSERT ( condition ) * * level - An integer known at compile time. * condition tested if level >= CHECK * * condition - The expression to test - * + * * e.g. ASSERT2( condition ) will only test condition if CHECK >= 2 - * + * */ #ifndef BOUT_ASSERT_H @@ -40,6 +40,7 @@ if (!(condition)) { \ throw BoutException("Assertion failed in {:s}, line {:d}: {:s}", __FILE__, __LINE__, \ #condition); \ + abort(); \ } #else // CHECKLEVEL >= 1 #define ASSERT1(condition) diff --git a/include/bout/boundary_iterator.hxx b/include/bout/boundary_iterator.hxx new file mode 100644 index 0000000000..bf03e15636 --- /dev/null +++ b/include/bout/boundary_iterator.hxx @@ -0,0 +1,229 @@ +#pragma once + +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" +#include "bout/mesh.hxx" +#include "bout/parallel_boundary_region.hxx" +#include "bout/region.hxx" +#include "bout/sys/parallel_stencils.hxx" +#include "bout/sys/range.hxx" +#include +#include + +class BoundaryRegionIter { +public: + virtual ~BoundaryRegionIter() = default; + BoundaryRegionIter(int x, int y, int bx, int by, const Mesh& mesh) + : _dir(bx + by), _x(x), _y(y), _bx(bx), _by(by), localmesh(&mesh) { + ASSERT3(_bx * _by == 0); + } + bool operator!=(const BoundaryRegionIter& rhs) const { return ind() != rhs.ind(); } + + Ind3D ind() const { return xyz2ind(_x, _y, _z); } + BoundaryRegionIter& operator++() { + ASSERT3(_z < nz()); + _z++; + if (_z == nz()) { + _z = 0; + _next(); + } + return *this; + } + +protected: + virtual void _next() = 0; + +public: + BoundaryRegionIter& operator*() { return *this; } + + void dirichlet_o2(Field3D& f, BoutReal value) const { + ynext(f) = bout::parallel_stencil::dirichlet_o2(1, f[ind()], 0.5, value); + } + + BoutReal extrapolate_grad_o2(const Field3D& f) const { return f[ind()] - yprev(f); } + + BoutReal extrapolate_sheath_o2(const Field3D& f) const { + return (f[ind()] * 3 - yprev(f)) * 0.5; + } + + BoutReal extrapolate_next_o2(const Field3D& f) const { + return (2 * f[ind()]) - yprev(f); + } + + BoutReal + extrapolate_next_o2(const std::function& f) const { + return (2 * f(0, ind())) - f(0, ind().yp(-_by).xp(-_bx)); + } + + BoutReal interpolate_sheath_o2(const Field3D& f) const { + return (f[ind()] + ynext(f)) * 0.5; + } + + BoutReal + interpolate_sheath_o2(const std::function& f) const { + return (f(0, ind()) + f(0, ind().yp(-_by).xp(-_bx))) * 0.5; + } + + BoutReal + extrapolate_sheath_o2(const std::function& f) const { + return 0.5 * (3 * f(0, ind()) - f(0, ind().yp(-_by).xp(-_bx))); + } + + BoutReal extrapolate_sheath_free(const Field3D& f, SheathLimitMode mode) const { + const BoutReal fac = + bout::parallel_boundary_region::limitFreeScale(yprev(f), ythis(f), mode); + const BoutReal val = ythis(f); + const BoutReal next = mode == SheathLimitMode::linear_free ? val + fac : val * fac; + return 0.5 * (val + next); + } + + void set_free(Field3D& f, SheathLimitMode mode) const { + const BoutReal fac = + bout::parallel_boundary_region::limitFreeScale(yprev(f), ythis(f), mode); + BoutReal val = ythis(f); + if (mode == SheathLimitMode::linear_free) { + for (int i = 1; i <= localmesh->ystart; ++i) { + val += fac; + f[ind().yp(_by * i).xp(_bx * i)] = val; + } + } else { + for (int i = 1; i <= localmesh->ystart; ++i) { + val *= fac; + f[ind().yp(_by * i).xp(_bx * i)] = val; + } + } + } + + void limitFree(Field3D& f) const { + const BoutReal fac = + bout::parallel_boundary_region::limitFreeScale(yprev(f), ythis(f)); + BoutReal val = ythis(f); + for (int i = 1; i <= localmesh->ystart; ++i) { + val *= fac; + f[ind().yp(_by * i).xp(_bx * i)] = val; + } + } + + bool is_lower() const { + ASSERT2(_bx == 0); + return _by == -1; + } + + void neumann_o1(Field3D& f, BoutReal grad) const { + BoutReal val = ythis(f); + for (int i = 1; i <= localmesh->ystart; ++i) { + val += grad; + f[ind().yp(_by * i).xp(_bx * i)] = val; + } + } + + void neumann_o2(Field3D& f, BoutReal grad) const { + BoutReal val = yprev(f) + grad; + for (int i = 1; i <= localmesh->ystart; ++i) { + val += grad; + f[ind().yp(_by * i).xp(_bx * i)] = val; + } + } + + void limit_at_least(Field3D& f, BoutReal value) const { + ynext(f) = std::max(ynext(f), value); + } + + BoutReal& ynext(Field3D& f) const { return f[ind().yp(_by).xp(_bx)]; } + const BoutReal& ynext(const Field3D& f) const { return f[ind().yp(_by).xp(_bx)]; } + BoutReal& yprev(Field3D& f) const { return f[ind().yp(-_by).xp(-_bx)]; } + const BoutReal& yprev(const Field3D& f) const { return f[ind().yp(-_by).xp(-_bx)]; } + BoutReal& ythis(Field3D& f) const { return f[ind()]; } + const BoutReal& ythis(const Field3D& f) const { return f[ind()]; } + + void setYPrevIfValid(Field3D& f, BoutReal val) const { yprev(f) = val; } + void setAll(Field3D& f, const BoutReal val) const { + for (int i = -localmesh->ystart; i <= localmesh->ystart; ++i) { + f[ind().yp(_by * i).xp(_bx * i)] = val; + } + } + + static int abs_offset() { return 1; } + + BoutReal& ynext(Field2D& f) const { return f[ind().yp(_by).xp(_bx)]; } + const BoutReal& ynext(const Field2D& f) const { return f[ind().yp(_by).xp(_bx)]; } + BoutReal& yprev(Field2D& f) const { return f[ind().yp(-_by).xp(-_bx)]; } + const BoutReal& yprev(const Field2D& f) const { return f[ind().yp(-_by).xp(-_bx)]; } + + int dir() const { return _dir; } + +protected: + int x() const { return _x; } + int y() const { return _y; } + int z() const { return _z; } + + void setx(int x) { _x = x; } + void sety(int y) { _y = y; } + +private: + int _dir; + + int _z{0}; + int _x; + int _y; + int _bx; + int _by; + + const Mesh* localmesh; + int nx() const { return localmesh->LocalNx; } + int ny() const { return localmesh->LocalNy; } + int nz() const { return localmesh->LocalNz; } + + Ind3D xyz2ind(int x, int y, int z) const { + return Ind3D{((x * ny() + y) * nz()) + z, ny(), nz()}; + } +}; + +class BoundaryRegionIterY : public BoundaryRegionIter { +public: + ~BoundaryRegionIterY() override = default; + BoundaryRegionIterY(const RangeIterator& r, int y, int dir, bool is_end, + const Mesh& mesh) + : BoundaryRegionIter(r.ind, y, 0, dir, mesh), r(r), is_end(is_end) {} + + bool operator!=(const BoundaryRegionIterY& rhs) { + ASSERT2(y() == rhs.y()); + if (is_end) { + if (rhs.is_end) { + return false; + } + return !rhs.r.isDone(); + } + if (rhs.is_end) { + return !r.isDone(); + } + return x() != rhs.x(); + } + + void _next() override { + ++r; + setx(r.ind); + } + +private: + RangeIterator r; + bool is_end; +}; + +class NewBoundaryRegionY { +public: + NewBoundaryRegionY(const Mesh& mesh, bool lower, const RangeIterator& r) + : mesh(&mesh), lower(lower), r(r) {} + BoundaryRegionIterY begin(bool begin = true) { + return BoundaryRegionIterY(r, lower ? mesh->ystart : mesh->yend, lower ? -1 : +1, + !begin, *mesh); + } + BoundaryRegionIterY end() { return begin(false); } + +private: + const Mesh* mesh; + bool lower; + RangeIterator r; +}; diff --git a/include/bout/boundary_op.hxx b/include/bout/boundary_op.hxx index 1a9aa1ad68..e4d2c459bf 100644 --- a/include/bout/boundary_op.hxx +++ b/include/bout/boundary_op.hxx @@ -55,9 +55,7 @@ public: // Note: All methods must implement clone, except for modifiers (see below) virtual BoundaryOp* clone(BoundaryRegion* UNUSED(region), - const std::list& UNUSED(args)) { - throw BoutException("BoundaryOp::clone not implemented"); - } + const std::list& UNUSED(args)) = 0; /// Clone using positional args and keywords /// If not implemented, check if keywords are passed, then call two-argument version @@ -88,6 +86,10 @@ public: BoundaryModifier() = default; BoundaryModifier(BoundaryOp* operation) : BoundaryOp(operation->bndry), op(operation) {} virtual BoundaryOp* cloneMod(BoundaryOp* op, const std::list& args) = 0; + BoundaryOp* clone(BoundaryRegion* UNUSED(region), + const std::list& UNUSED(args)) override { + throw BoutException("This must not be used!"); + } protected: BoundaryOp* op{nullptr}; diff --git a/include/bout/boundary_region.hxx b/include/bout/boundary_region.hxx index 58de12045e..739d06f11f 100644 --- a/include/bout/boundary_region.hxx +++ b/include/bout/boundary_region.hxx @@ -4,6 +4,7 @@ class BoundaryRegion; #ifndef BOUT_BNDRY_REGION_H #define BOUT_BNDRY_REGION_H +#include #include #include @@ -15,16 +16,17 @@ extern Mesh* mesh; ///< Global mesh } // namespace bout /// Location of boundary -enum class BndryLoc { - xin, - xout, - ydown, - yup, - all, - par_fwd_xin, // Don't include parallel boundaries - par_bkwd_xin, - par_fwd_xout, // Don't include parallel boundaries - par_bkwd_xout +enum class BndryLoc : std::int8_t { + xin = 0, + xout = 1, + ydown = 2, + yup = 3, + all = 4, + par_fwd_xin = 5, // Don't include parallel boundaries + par_bkwd_xin = 6, + par_fwd_xout = 7, // Don't include parallel boundaries + par_bkwd_xout = 8, + invalid = -1 }; constexpr BndryLoc BNDRY_XIN = BndryLoc::xin; constexpr BndryLoc BNDRY_XOUT = BndryLoc::xout; @@ -35,6 +37,10 @@ constexpr BndryLoc BNDRY_PAR_FWD_XIN = BndryLoc::par_fwd_xin; constexpr BndryLoc BNDRY_PAR_BKWD_XIN = BndryLoc::par_bkwd_xin; constexpr BndryLoc BNDRY_PAR_FWD_XOUT = BndryLoc::par_fwd_xout; constexpr BndryLoc BNDRY_PAR_BKWD_XOUT = BndryLoc::par_bkwd_xout; +constexpr BndryLoc BNDRY_INVALID = BndryLoc::invalid; + +/// Physical type of y boundary +enum class YBndryType : std::int8_t { sheath, not_sheath, all }; class BoundaryRegionBase { public: @@ -45,14 +51,17 @@ public: : localmesh(passmesh ? passmesh : bout::globals::mesh), label(std::move(name)), location(loc) {} - virtual ~BoundaryRegionBase() = default; + virtual ~BoundaryRegionBase(); + virtual BoundaryRegion* getLegacyPointer(); Mesh* localmesh; ///< Mesh does this boundary region belongs to std::string label; ///< Label for this boundary region - BndryLoc location; ///< Which side of the domain is it on? - bool isParallel = false; ///< Is this a parallel boundary? + BndryLoc location{BndryLoc::invalid}; ///< Which side of the domain is it on? + bool isParallel = false; ///< Is this a parallel boundary? + bool isX = false; + bool isY = false; virtual void first() = 0; ///< Move the region iterator to the start virtual void next() = 0; ///< Get the next element in the loop @@ -60,6 +69,8 @@ public: /// X or Y first) virtual bool isDone() = 0; ///< Returns true if outside domain. Can use this with nested nextX, nextY + + BoundaryRegion* legacy{nullptr}; }; /// Describes a region of the boundary, and a means of iterating over it @@ -71,6 +82,7 @@ public: BoundaryRegion(std::string name, int xd, int yd, Mesh* passmesh = nullptr) : BoundaryRegionBase(name, passmesh), bx(xd), by(yd), width(2) {} ~BoundaryRegion() override = default; + BoundaryRegion* getLegacyPointer() override { return this; } int x, y; ///< Indices of the point in the boundary int bx, by; ///< Direction of the boundary [x+bx][y+by] is going outwards diff --git a/include/bout/boundary_region_iter.hxx b/include/bout/boundary_region_iter.hxx new file mode 100644 index 0000000000..242306c04f --- /dev/null +++ b/include/bout/boundary_region_iter.hxx @@ -0,0 +1,785 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/field_data.hxx" +#include "bout/utils.hxx" +#include +#include +#include +#include +#include + +namespace bout { +namespace boundary { + +/// Physical type of y boundary +enum class BndryType : std::int8_t { + sheath, + not_sheath_par, + core, + sol, + sol_perp, + sol_par, + all, + num +}; + +/// Types of free boundary condition +/// ================== =================================================== +/// Name Description +/// ================== =================================================== +/// ``limited`` use exponential if decreasing, otherwise Neumanm +/// ``exponential`` use exponential extrapolation +/// ``linear`` use linear extrapolation +/// ================== =================================================== +enum class BoundaryFreeExtrapolation : std::int8_t { limited, exponential, linear }; +//BOUT_ENUM_CLASS(BoundaryFreeExtrapolation, limited, exponential, linear); + +template +class BoundaryRegionIterBase { + BoundaryRegionIterBase() = default; + /// get the index at the last point in domain +public: + Ind3D ind() const { return static_cast(this)->_ind(); } + /// get the length from the point in the domain to the boundary in index + /// space. It is in the range [0, 1] + BoutReal length(CELL_LOC loc) const { + return static_cast(this)->_length(loc); + } + /// Lower bound of how many points are between the first point in the domain + /// and the boundary in the other direction. + signed char valid() const { return static_cast(this)->_valid(); } + /// Get the width of the boundary at the current point + int boundary_width() const { return static_cast(this)->_boundary_width(); } + /// Is this the lower boundary? + bool is_lower() const { return static_cast(this)->_is_lower(); } + + /* + * FIELD3D ACCESSORS + */ + + /// get the value at a given offset `off` of a field `f`. + /// off = -1 is the second point in the boundary + /// off = 0 is the first point in the boundary + /// off = 1 is the last point in the domain + /// off = 2 is the second to last point in the domain + template + BoutReal& getAt(Field3D& f, int off) const { + return static_cast(this)->template _getAt(f, off); + } + /// get the value at a given offset `off` of a field `f`. + template + BoutReal& getAt(const Field3D& f, int off) const { + return static_cast(this)->template _getAt(f, off); + } + + /// Get the first point in the boundary + const BoutReal& next(const Field3D& f) const { + return static_cast(this)->_getAt(f, 0); + } + /// Get the first point in the boundary + BoutReal& next(Field3D& f) const { + return static_cast(this)->_getAt(f, 0); + } + /// Get the last point in the domain + const BoutReal& current(const Field3D& f) const { + return static_cast(this)->_getAt(f, 1); + } + /// Get the last point in the domain + BoutReal& current(Field3D& f) const { + return static_cast(this)->_getAt(f, 1); + } + /// Get the second to last point in the domain - this may not be valid and thus throw + const BoutReal& prev(const Field3D& f) const { + return static_cast(this)->_getAt(f, 2); + } + /// Get the offset from the last point in the domain + /// For FA this is always ±1, for FCI this can be up to ±MYG, excluding 0 + int offset() const { return static_cast(this)->_offset(); } + /* + * FIELD2D ACCESSORS + */ + + /// get the value at a given offset `off` of a field `f`. + /// off = -1 is the second point in the boundary + /// off = 0 is the first point in the boundary + /// off = 1 is the last point in the domain + /// off = 2 is the second to last point in the domain + template + BoutReal& getAt(Field2D& f, int off) const { + return static_cast(this)->template _getAt(f, off); + } + /// get the value at a given offset `off` of a field `f`. + template + BoutReal& getAt(const Field2D& f, int off) const { + return static_cast(this)->template _getAt(f, off); + } + + /// Get the first point in the boundary + const BoutReal& next(const Field2D& f) const { + return static_cast(this)->_getAt(f, 0); + } + /// Get the first point in the boundary + BoutReal& next(Field2D& f) const { + return static_cast(this)->_getAt(f, 0); + } + /// Get the last point in the domain + const BoutReal& current(const Field2D& f) const { + return static_cast(this)->_getAt(f, 1); + } + /// Get the last point in the domain + BoutReal& current(Field2D& f) const { + return static_cast(this)->_getAt(f, 1); + } + /// Get the second to last point in the domain - this may not be valid and thus throw + const BoutReal& prev(const Field2D& f) const { + return static_cast(this)->_getAt(f, 2); + } + + /* + * FUNCTIONS ACCESSORS + */ + + /// get the value at a given offset `off` of a field `f`. + /// off = -1 is the second point in the boundary + /// off = 0 is the first point in the boundary + /// off = 1 is the last point in the domain + /// off = 2 is the second to last point in the domain + template + BoutReal& getAt(const std::function& func, + int off) const { + return static_cast(this)->template _getAt(func, off); + } + /// Get the first point in the boundary + const BoutReal& + next(const std::function& func) const { + return static_cast(this)->_getAt(func, 0); + } + /// Get the last point in the domain + const BoutReal& + current(const std::function& func) const { + return static_cast(this)->_getAt(func, 1); + } + /// Get the second to last point in the domain - this may not be valid and thus throw + const BoutReal& + prev(const std::function& func) const { + return static_cast(this)->_getAt(func, 2); + } + + /* + * INTERPOLATION and EXTRAPOLATION + */ + + // extrapolate a given field to the boundary + BoutReal extrapolate_boundary_o1(const Field3D& f) const { return current(f); } + // extrapolate a given field to the boundary + BoutReal extrapolate_boundary_o2(const Field3D& f) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return extrapolate_boundary_o1(f); + } + return current(f) * (1 + length(f.getLocation())) - prev(f) * length(f.getLocation()); + } + /// Extrapolate a given function to the boundary + BoutReal + extrapolate_bounday_o1(const std::function& func, + [[maybe_unused]] CELL_LOC loc = CELL_CENTRE) const { + return current(func); + } + /// Extrapolate a given function to the boundary + BoutReal + extrapolate_boundary_o2(const std::function& func, + CELL_LOC loc = CELL_CENTRE) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return extrapolate_boundary_o1(func); + } + return current(func) * (1 + length(loc)) - prev(func) * length(loc); + } + + /// Interpolate a field to the boundary, using the boundary values + BoutReal interpolate_boundary_o2(const Field3D& f) const { + return current(f) * (1 - length(f.getLocation())) + next(f) * length(f.getLocation()); + } + /// Interpolate a field to the boundary, using the boundary values + BoutReal + interpolate_boundary_o2(const std::function& func, + CELL_LOC loc = CELL_CENTRE) const { + return current(func) * (1 - length(loc)) + next(func) * length(loc); + } + /// Extrapolate to the first boundary value freely + BoutReal extrapolate_next_o1(const Field3D& f) const { return current(f); } + /// Extrapolate to the first boundary value freely + BoutReal extrapolate_next_o2(const Field3D& f) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return extrapolate_next_o1(f); + } + return current(f) * 2 - prev(f); + } + + /// Extrapolate to the first boundary value freely + BoutReal + extrapolate_next_o1(const std::function& func) const { + return current(func); + } + /// Extrapolate to the first boundary value freely + BoutReal + extrapolate_next_o2(const std::function& func) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return extrapolate_next_o1(func); + } + return current(func) * 2 - prev(func); + } + + /// extrapolate the gradient into the boundary + BoutReal extrapolate_grad_o1([[maybe_unused]] const Field3D& f) const { return 0; } + /// extrapolate the gradient into the boundary + BoutReal extrapolate_grad_o2(const Field3D& f) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return extrapolate_grad_o1(f); + } + return current(f) - next(f); + } + + BoutReal extrapolate_boundary_free(const Field3D& f, + BoundaryFreeExtrapolation mode) const { + BoutReal fac = BoutNaN; + if (valid() > 0) { + fac = limitFreeScale(prev(f), current(f), mode); + } else { + fac = mode == BoundaryFreeExtrapolation::linear ? 0 : 1; + } + auto val = current(f); + BoutReal next = mode == BoundaryFreeExtrapolation::linear ? val + fac : val * fac; + return val * length(f.getLocation()) + next * (1 - length(f.getLocation())); + } + + /* + * APPLY BOUNDARY CONDITIONS + */ + + /// Apply a dirichlet boundary condition + void dirichlet_o1(Field3D& f, BoutReal value) const { + for (int i = 0; i < boundary_width(); ++i) { + getAt(f, -i) = value; + } + } + + /// Apply a dirichlet boundary condition + void dirichlet_o2(Field3D& f, BoutReal value) const { + if (length(f.getLocation()) < small_value) { + return dirichlet_o1(f, value); + } + for (int i = 0; i < boundary_width(); ++i) { + getAt(f, -i) = parallel_stencil::dirichlet_o2( + i + 1, current(f), i + 1 - length(f.getLocation()), value); + } + } + + /// Apply a dirichlet boundary condition + void dirichlet_o3(Field3D& f, BoutReal value) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return dirichlet_o2(f, value); + } + if (length(f.getLocation()) < small_value) { + for (int i = 0; i < boundary_width(); ++i) { + getAt(f, -i) = parallel_stencil::dirichlet_o2( + i + 2, prev(f), i + 1 - length(f.getLocation()), value); + } + } else { + for (int i = 0; i < boundary_width(); ++i) { + getAt(f, -i) = parallel_stencil::dirichlet_o3( + i + 2, prev(f), i + 1, current(f), i + 1 - length(f.getLocation()), value); + } + } + } + + /// Ensure the value in the boundary is at least `value` + void limit_at_least(Field3D& f, BoutReal value) const { + for (int i = 0; i < boundary_width(); ++i) { + if (getAt(f, -i) < value) { + getAt(f, -i) = value; + } + } + } + + /// Apply neumann boundary condition, where `value` is the gradient in index space + + // neumann_o1 would give second order convergence, given an appropriate one-sided stencil. + // But in general we do not, and thus for normal C2 stencils, this is 1st order. + void neumann_o1(Field3D& f, BoutReal value) const { + for (int i = 0; i < boundary_width(); ++i) { + getAt(f, -i) = current(f) + value * (i + 1); + } + } + + /// Apply neumann boundary condition, where `value` is the gradient in index space + void neumann_o2(Field3D& f, BoutReal value) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return neumann_o1(f, value); + } + for (int i = 0; i < boundary_width(); ++i) { + getAt(f, -i) = prev(f) + (2 + i) * value; + } + } + + /// Apply neumann boundary condition, where `value` is the gradient in index space + void neumann_o3(Field3D& f, BoutReal value) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return neumann_o2(f, value); + } + for (int i = 0; i < boundary_width(); ++i) { + getAt(f, -i) = parallel_stencil::neumann_o3(i + 1 - length(f.getLocation()), value, + i + 1, current(f), 2, prev(f)); + } + } + + void set_free(Field3D& f, BoundaryFreeExtrapolation mode) const { + BoutReal fac = BoutNaN; + if (valid() > 0) { + fac = limitFreeScale(prev(f), current(f), mode); + } else { + fac = mode == BoundaryFreeExtrapolation::linear ? 0 : 1; + } + auto val = current(f); + if (mode == BoundaryFreeExtrapolation::linear) { + for (int i = 0; i < boundary_width(); ++i) { + val += fac; + getAt(f, -i) = val; + } + } else { + for (int i = 0; i < boundary_width(); ++i) { + val *= fac; + getAt(f, -i) = val; + } + } + } + void setSmallValue(BoutReal val) { + ASSERT2(val > 0); + ASSERT2(val < 0.5); + small_value = val; + } + +private: + BoutReal small_value = 1e-4; + friend impl; +}; + +namespace { +/// Limited free gradient of log of a quantity +/// This ensures that the guard cell values remain positive +/// while also ensuring that the quantity never increases +/// +/// fm fc | fp +/// ^ boundary +/// +/// exp( 2*log(fc) - log(fm) ) +inline BoutReal limitFreeScale(BoutReal fm, BoutReal fc, BoundaryFreeExtrapolation mode) { + if ((fm < fc) && (mode == BoundaryFreeExtrapolation::limited)) { + return fc; // Neumann rather than increasing into boundary + } + if (fm < 1e-10) { + return fc; // Low / no density condition + } + + BoutReal fp = 0; + switch (mode) { + case BoundaryFreeExtrapolation::limited: + case BoundaryFreeExtrapolation::exponential: + fp = SQ(fc) / fm; // Exponential + break; + case BoundaryFreeExtrapolation::linear: + fp = (2.0 * fc) - fm; // Linear + break; + } + +#if CHECKLEVEL >= 2 + if (!std::isfinite(fp)) { + throw BoutException("SheathBoundary limitFree {}: {}, {} -> {}", + static_cast(mode), fm, fc, fp); + } +#endif + + return fp; +} +} // namespace + +class BoundaryRegionFCI : public BoundaryRegionBase { +public: + BoundaryRegionFCI(const std::string& name, const BndryLoc& loc, int dir, Mesh* mesh) + : BoundaryRegionBase(name, loc, mesh), _dir(dir), localmesh(mesh) { + isParallel = true; + }; + /// Add a point to the boundary + void add_point(Ind3D ind, BoutReal x, BoutReal y, BoutReal z, BoutReal length, + char valid, signed char offset) { + if (!bndry_points.empty() && bndry_points.back().index > ind) { + is_sorted = false; + } + bndry_points.emplace_back(ind, bout::parallel_boundary_region::RealPoint{x, y, z}, + length, valid, offset, + static_cast(std::abs(offset))); + } + void add_point(int ix, int iy, int iz, BoutReal x, BoutReal y, BoutReal z, + BoutReal length, char valid, signed char offset) { + add_point(xyz2ind(ix, iy, iz), x, y, z, length, valid, offset); + } + bool contains(int ix, int iy, int iz) { + const auto ind = xyz2ind(ix, iy, iz); + ensureSorted(); + const auto found = + std::lower_bound(std::begin(bndry_points), std::end(bndry_points), ind); + return found != std::end(bndry_points) and found->index == ind; + } + int dir() const { return _dir; } + // legacy interface + void first() override { throw BoutException("Legacy interface is not suppored"); } + void next() override { throw BoutException("Legacy interface is not suppored"); } + bool isDone() override { throw BoutException("Legacy interface is not suppored"); } + +private: + friend class BoundaryRegionIterFCI; + int _dir; + // Vector of points in the boundary + bout::parallel_boundary_region::IndicesVec bndry_points; + Mesh* localmesh; + bool is_sorted{true}; + void ensureSorted() { + if (is_sorted) { + return; + } + std::sort(std::begin(bndry_points), std::end(bndry_points)); + } + Ind3D xyz2ind(int x, int y, int z) const { + const int ny = localmesh->LocalNy; + const int nz = localmesh->LocalNz; + return Ind3D{((x * ny + y) * nz) + z, ny, nz}; + } +}; + +class BoundaryRegionIterFCI : public BoundaryRegionIterBase { +private: + // TODO(dave) make non-const? + const BoundaryRegionFCI* region; + size_t pos{0}; + +public: + BoundaryRegionIterFCI() = delete; + BoundaryRegionIterFCI(const BoundaryRegionFCI* reg, bool isstart) + : region(reg), pos(isstart ? 0 : reg->bndry_points.size()) {} + void setValid(char valid) { + const_cast(region)->bndry_points[pos].valid = valid; + }; + BoutReal s_x() const { return region->bndry_points[pos].intersection.s_x; }; + BoutReal s_y() const { return region->bndry_points[pos].intersection.s_y; }; + BoutReal s_z() const { return region->bndry_points[pos].intersection.s_z; }; + Mesh* localmesh() const { return region->localmesh; }; + int dir() const { return region->_dir; } + template + BoutReal& _getAt(Field3D& f, int off) const { + ASSERT3(f.hasParallelSlices()); + if constexpr (check) { + ASSERT3(_valid() > -off - 2); + } + auto _off = _offset() - (off * region->_dir); + return f.ynext(_off)[_ind().yp(_off)]; + } + template + const BoutReal& _getAt(const Field3D& f, int off) const { + ASSERT3(f.hasParallelSlices()); + if constexpr (check) { + ASSERT3(_valid() > -off - 2); + } + auto _off = _offset() - (off * region->_dir); + return f.ynext(_off)[_ind().yp(_off)]; + } + template + BoutReal& _getAt(Field2D& f, int off) const { + ASSERT3(f.hasParallelSlices()); + if constexpr (check) { + ASSERT3(_valid() > -off - 2); + } + auto _off = _offset() - (off * region->_dir); + return f.ynext(_off)[_ind().yp(_off)]; + } + template + const BoutReal& _getAt(const Field2D& f, int off) const { + ASSERT3(f.hasParallelSlices()); + if constexpr (check) { + ASSERT3(_valid() > -off - 2); + } + auto _off = _offset() - (off * region->_dir); + return f.ynext(_off)[_ind().yp(_off)]; + } + template + BoutReal getAt(const std::function& f, + int off) const { + if constexpr (check) { + ASSERT3(valid() > -off - 2); + } + auto _off = _offset() + (off * region->_dir); + return f(_off, _ind().yp(_off)); + } + signed char _offset() const { return region->bndry_points[pos].offset; } + signed char _valid() const { return region->bndry_points[pos].valid; } + Ind3D _ind() const { return region->bndry_points[pos].index; } + int _boundary_width() const { + return region->localmesh->ystart - region->bndry_points[pos].abs_offset + 1; + } + BoutReal _length([[maybe_unused]] CELL_LOC loc) const { + ASSERT3(loc == CELL_CENTRE); + return region->bndry_points[pos].length; + } + bool operator!=(BoundaryRegionIterFCI lhs) const { + ASSERT3(region == lhs.region); + return pos != lhs.pos; + } + BoundaryRegionIterFCI& operator++() { + ++pos; + return *this; + } + // No-op for compatibility + BoundaryRegionIterFCI& operator*() { return *this; } +}; + +template +class BoundaryRegionXY : public BoundaryRegionBase { +public: + BoundaryRegionXY() = delete; + BoundaryRegionXY(const std::string& name, int dir, Mesh* mesh, Region&& rgn) + : BoundaryRegionBase(name, mesh), _dir(dir), + valid(isXtemp ? mesh->xstart : mesh->ystart) { + BOUT_FOR_SERIAL(i, rgn) { this->rgn.emplace_back(i); } + if constexpr (isXtemp) { + this->isX = true; + location = dir == 1 ? BNDRY_XOUT : BNDRY_XIN; + } else { + this->isY = true; + location = dir == 1 ? BNDRY_YDOWN : BNDRY_YUP; + } + } + int dir() { return _dir; } + // legacy interface + void first() override { throw BoutException("Legacy interface is not suppored"); } + void next() override { throw BoutException("Legacy interface is not suppored"); } + bool isDone() override { throw BoutException("Legacy interface is not suppored"); } + +private: + template + friend class BoundaryRegionIterXY; + int _dir; + std::vector rgn; + signed char valid; +}; + +template +class BoundaryRegionIterXY : public BoundaryRegionIterBase> { +private: + const BoundaryRegionXY* region; + size_t pos{0}; + +public: + BoundaryRegionIterXY() = delete; + BoundaryRegionIterXY(const BoundaryRegionXY* reg, bool isstart) + : region(reg), pos(isstart ? 0 : reg->rgn.size()) {} + int dir() const { return region->_dir; } + template + BoutReal& _getAt(Field3D& f, int off) const { + if constexpr (check) { + ASSERT3(_valid() > -off - 2); + } + auto _off = (1 - off) * region->_dir; + if constexpr (isX) { + return f[_ind().xp(_off)]; + } else { + return f[_ind().yp(_off)]; + } + } + template + const BoutReal& _getAt(const Field3D& f, int off) const { + if constexpr (check) { + ASSERT3(_valid() > -off - 2); + } + auto _off = (1 - off) * region->_dir; + if constexpr (isX) { + return f[_ind().xp(_off)]; + } else { + return f[_ind().yp(_off)]; + } + } + template + BoutReal& _getAt(Field2D& f, int off) const { + if constexpr (check) { + ASSERT3(_valid() > -off - 2); + } + auto _off = (1 - off) * region->_dir; + if constexpr (isX) { + return f[_ind().xp(_off)]; + } else { + return f[_ind().yp(_off)]; + } + } + template + const BoutReal& _getAt(const Field2D& f, int off) const { + if constexpr (check) { + ASSERT3(_valid() > -off - 2); + } + auto _off = (1 - off) * region->_dir; + if constexpr (isX) { + return f[_ind().xp(_off)]; + } else { + return f[_ind().yp(_off)]; + } + } + template + BoutReal getAt(const std::function& f, + int off) const { + if constexpr (check) { + ASSERT3(_valid() > -off - 2); + } + auto _off = (1 - off) * region->_dir; + if constexpr (isX) { + return f(0, _ind().xp(_off)); + } else { + return f(0, _ind().yp(_off)); + } + } + signed char _offset() const { return region->_dir; } + signed char _valid() const { return region->valid; } + Ind3D _ind() const { return region->rgn[pos]; } + int _boundary_width() const { + if constexpr (isX) { + return region->localmesh->xstart; + } + return region->localmesh->ystart; + } + BoutReal _length(CELL_LOC loc) const { + if (loc == CELL_XLOW) { + if (dir() == 1) { + return 1; + } + return 0; + } + return 0.5; + } + bool operator!=(BoundaryRegionIterXY lhs) { + ASSERT3(region == lhs.region); + return pos != lhs.pos; + } + BoundaryRegionIterXY& operator++() { + ++pos; + return *this; + } + // No-op for compatibility + BoundaryRegionIterXY& operator*() { return *this; } +}; +using BoundaryRegionX = BoundaryRegionXY; +using BoundaryRegionY = BoundaryRegionXY; +using BoundaryRegionIterX = BoundaryRegionIterXY; +using BoundaryRegionIterY = BoundaryRegionIterXY; + +inline BoundaryRegionX* NewBoundaryRegionXIn(const std::string& name, int ymin, int ymax, + Mesh* mesh) { + auto* pointer = new BoundaryRegionX( + name, -1, mesh, + Region(mesh->xstart, mesh->xstart, ymin, ymax, mesh->zstart, mesh->zend, + mesh->LocalNy, mesh->LocalNz, mesh->maxregionblocksize)); + pointer->legacy = new ::BoundaryRegionXIn(name, ymin, ymax, mesh); + return pointer; +} + +inline BoundaryRegionX* NewBoundaryRegionXOut(const std::string& name, int ymin, int ymax, + Mesh* mesh) { + auto* pointer = new BoundaryRegionX( + name, 1, mesh, + Region(mesh->xend, mesh->xend, ymin, ymax, mesh->zstart, mesh->zend, + mesh->LocalNy, mesh->LocalNz, mesh->maxregionblocksize)); + pointer->legacy = new ::BoundaryRegionXOut(name, ymin, ymax, mesh); + return pointer; +} + +inline BoundaryRegionY* NewBoundaryRegionYUp(const std::string& name, int xmin, int xmax, + Mesh* mesh) { + auto* pointer = new BoundaryRegionY( + name, -1, mesh, + Region(xmin, xmax, mesh->yend, mesh->yend, mesh->zstart, mesh->zend, + mesh->LocalNy, mesh->LocalNz, mesh->maxregionblocksize)); + pointer->legacy = new ::BoundaryRegionYUp(name, xmin, xmax, mesh); + return pointer; +} + +inline BoundaryRegionY* NewBoundaryRegionYDown(const std::string& name, int xmin, + int xmax, Mesh* mesh) { + auto* pointer = new BoundaryRegionY( + name, 1, mesh, + Region(xmin, xmax, mesh->ystart, mesh->ystart, mesh->zstart, mesh->zend, + mesh->LocalNy, mesh->LocalNz, mesh->maxregionblocksize)); + pointer->legacy = new ::BoundaryRegionYDown(name, xmin, xmax, mesh); + return pointer; +} + +template +void iter_boundary(const BoundaryRegionBase* bndrybase, const Func& func) { + if (bndrybase->isX) { + const auto* const bndry = dynamic_cast(bndrybase); + return iter_boundary(*bndry, func); + } + if (bndrybase->isY) { + const auto* const bndry = dynamic_cast(bndrybase); + return iter_boundary(*bndry, func); + } + if (bndrybase->isParallel) { + const auto* const bndry = dynamic_cast(bndrybase); + return iter_boundary(*bndry, func); + } + throw BoutException("{} is of unknown type - probably a legacy iterator", + bndrybase->label); +} + +template ::value>> +void iter_boundary(const Bndry& bndry, const Func& func) { + static_assert(std::is_base_of::value, + "Bndry must derive from BoundaryRegionY"); + for (auto& point : bndry) { + func(point); + } +} + +} // namespace boundary +} // namespace bout +inline bout::boundary::BoundaryRegionIterFCI +begin(const bout::boundary::BoundaryRegionFCI& reg) { + return bout::boundary::BoundaryRegionIterFCI(®, true); +} +inline bout::boundary::BoundaryRegionIterFCI +end(const bout::boundary::BoundaryRegionFCI& reg) { + return bout::boundary::BoundaryRegionIterFCI(®, false); +} + +template +inline bout::boundary::BoundaryRegionIterXY +begin(const bout::boundary::BoundaryRegionXY& reg) { + return bout::boundary::BoundaryRegionIterXY(®, true); +} +template +inline bout::boundary::BoundaryRegionIterXY +end(const bout::boundary::BoundaryRegionXY& reg) { + return bout::boundary::BoundaryRegionIterXY(®, false); +} diff --git a/include/bout/bout.hxx b/include/bout/bout.hxx index 12c223e067..1c9147ceb6 100644 --- a/include/bout/bout.hxx +++ b/include/bout/bout.hxx @@ -34,6 +34,8 @@ #ifndef BOUT_H #define BOUT_H +#include // std::filesystem (C++17) + // IWYU pragma: begin_keep, begin_export #include "bout/build_defines.hxx" @@ -113,10 +115,10 @@ void setupGetText(); struct CommandLineArgs { int verbosity{4}; bool color_output{false}; - std::string data_dir{"data"}; ///< Directory for data input/output - std::string opt_file{"BOUT.inp"}; ///< Filename for the options file - std::string set_file{"BOUT.settings"}; ///< Filename for the options file - std::string log_file{"BOUT.log"}; ///< File name for the log file + std::filesystem::path data_dir{"data"}; ///< Directory for data input/output + std::filesystem::path opt_file{"BOUT.inp"}; ///< Filename for the options file + std::filesystem::path set_file{"BOUT.settings"}; ///< Filename for the options file + std::filesystem::path log_file{"BOUT.log"}; ///< File name for the log file /// The original set of command line arguments std::vector original_argv; /// The "canonicalised" command line arguments, with single-letter diff --git a/include/bout/bout_enum_class.hxx b/include/bout/bout_enum_class.hxx index 585e5b020e..334fa657da 100644 --- a/include/bout/bout_enum_class.hxx +++ b/include/bout/bout_enum_class.hxx @@ -2,7 +2,7 @@ * Copyright 2019 B.D.Dudson, J.T.Omotani, P.Hill * * Contact Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -22,13 +22,13 @@ #ifndef BOUT_ENUM_CLASS_H #define BOUT_ENUM_CLASS_H -#include "bout/boutexception.hxx" +#include "bout/boutexception.hxx" // IWYU pragma: keep #include "bout/macro_for_each.hxx" -#include "bout/msg_stack.hxx" -#include "bout/options.hxx" +#include "bout/options.hxx" // IWYU pragma: keep -#include -#include +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep /// Create some macro magic similar to bout/macro_for_each.hxx, but allowing for the enum /// class name to be passed through to each _call @@ -67,10 +67,10 @@ /// Create an enum class with toString and FromString functions, and an /// Options::as overload to read the enum #define BOUT_ENUM_CLASS(enumname, ...) \ - enum class enumname { __VA_ARGS__ }; \ + enum class enumname : std::int8_t { __VA_ARGS__ }; \ \ inline std::string toString(enumname e) { \ - AUTO_TRACE(); \ + \ const static std::map toString_map = { \ BOUT_ENUM_CLASS_MAP_ARGS(BOUT_ENUM_CLASS_STR, enumname, __VA_ARGS__)}; \ auto found = toString_map.find(e); \ @@ -81,16 +81,17 @@ } \ \ inline enumname BOUT_MAKE_FROMSTRING_NAME(enumname)(const std::string& s) { \ - AUTO_TRACE(); \ + \ const static std::map fromString_map = { \ BOUT_ENUM_CLASS_MAP_ARGS(BOUT_STR_ENUM_CLASS, enumname, __VA_ARGS__)}; \ auto found = fromString_map.find(s); \ if (found == fromString_map.end()) { \ - std::string valid_values {}; \ + std::string valid_values{}; \ for (auto const& entry : fromString_map) { \ valid_values += std::string(" ") + entry.first; \ } \ - throw BoutException("Did not find enum {:s}. Valid values: {:s}", s, valid_values); \ + throw BoutException("Did not find enum {:s}. Valid values: {:s}", s, \ + valid_values); \ } \ return found->second; \ } \ diff --git a/include/bout/bout_types.hxx b/include/bout/bout_types.hxx index c1f06fca7c..7747b937b3 100644 --- a/include/bout/bout_types.hxx +++ b/include/bout/bout_types.hxx @@ -1,8 +1,8 @@ /************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact Ben Dudson, dudson2@llnl.gov * - * Contact Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -22,6 +22,8 @@ #ifndef BOUT_TYPES_H #define BOUT_TYPES_H +#include "bout/build_config.hxx" + #include #include @@ -140,4 +142,15 @@ struct enumWrapper { /// Boundary condition function using FuncPtr = BoutReal (*)(BoutReal t, BoutReal x, BoutReal y, BoutReal z); +template +struct Constant { + T val; + struct View { + T v; + View(T v) : v(v) {} + BOUT_HOST_DEVICE T operator()(int) const { return v; } + }; + operator View() const { return {val}; } +}; + #endif // BOUT_TYPES_H diff --git a/include/bout/boutcomm.hxx b/include/bout/boutcomm.hxx index 68c7559fee..29bff53087 100644 --- a/include/bout/boutcomm.hxx +++ b/include/bout/boutcomm.hxx @@ -4,9 +4,9 @@ * * ************************************************************************** -* Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu +* Copyright 2010 - 2026 BOUT++ contributors * -* Contact: Ben Dudson, bd512@york.ac.uk +* Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -49,6 +49,8 @@ public: static int rank(); ///< Rank: my processor number static int size(); ///< Size: number of processors + static void abort(int errorcode); ///< MPI abort + // Setting options void setComm(MPI_Comm c); diff --git a/include/bout/boutexception.hxx b/include/bout/boutexception.hxx index 238c88654c..735f98c7d6 100644 --- a/include/bout/boutexception.hxx +++ b/include/bout/boutexception.hxx @@ -1,13 +1,11 @@ #ifndef BOUT_EXCEPTION_H #define BOUT_EXCEPTION_H -#include "bout/build_defines.hxx" - -#include #include #include #include +#include "fmt/base.h" #include "fmt/core.h" /// Throw BoutRhsFail with \p message if any one process has non-zero @@ -16,11 +14,16 @@ void BoutParallelThrowRhsFail(int status, const char* message); class BoutException : public std::exception { public: + BoutException(const BoutException&) = default; + BoutException(BoutException&&) = delete; + BoutException& operator=(const BoutException&) = default; + BoutException& operator=(BoutException&&) = delete; BoutException(std::string msg); - template - BoutException(const S& format, const Args&... args) - : BoutException(fmt::format(format, args...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + BoutException(fmt::format_string format, Args&&... args) + : BoutException(fmt::vformat(format, fmt::make_format_args(args...))) {} ~BoutException() override; @@ -30,32 +33,31 @@ public: /// backtrace (if available) std::string getBacktrace() const; + static void enableBacktrace() { show_backtrace = true; } + static void disableBacktrace() { show_backtrace = false; } + private: std::string message; -#if BOUT_USE_BACKTRACE - static constexpr unsigned int TRACE_MAX = 128; - std::array trace{}; - int trace_size; - char** messages; -#endif - void makeBacktrace(); + static bool show_backtrace; }; class BoutRhsFail : public BoutException { public: BoutRhsFail(std::string message) : BoutException(std::move(message)) {} - template - BoutRhsFail(const S& format, const Args&... args) - : BoutRhsFail(fmt::format(format, args...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + BoutRhsFail(fmt::format_string format, Args&&... args) + : BoutRhsFail(fmt::vformat(format, fmt::make_format_args(args...))) {} }; class BoutIterationFail : public BoutException { public: BoutIterationFail(std::string message) : BoutException(std::move(message)) {} - template - BoutIterationFail(const S& format, const Args&... args) - : BoutIterationFail(fmt::format(format, args...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + BoutIterationFail(fmt::format_string format, Args&&... args) + : BoutIterationFail(fmt::vformat(format, fmt::make_format_args(args...))) {} }; #endif diff --git a/include/bout/build_config.hxx b/include/bout/build_config.hxx index 08158d00e9..aa7f257ca5 100644 --- a/include/bout/build_config.hxx +++ b/include/bout/build_config.hxx @@ -30,6 +30,7 @@ constexpr auto has_uuid_system_generator = static_cast(BOUT_HAS_UUID_SYSTEM_GENERATOR); constexpr auto has_slepc = static_cast(BOUT_HAS_SLEPC); constexpr auto has_sundials = static_cast(BOUT_HAS_SUNDIALS); +constexpr auto has_sundials_manyvector = static_cast(BOUT_HAS_SUNDIALS_MANYVECTOR); constexpr auto use_backtrace = static_cast(BOUT_USE_BACKTRACE); constexpr auto use_color = static_cast(BOUT_USE_COLOR); constexpr auto use_openmp = static_cast(BOUT_USE_OPENMP); @@ -47,14 +48,50 @@ constexpr auto use_msgstack = static_cast(BOUT_USE_MSGSTACK); #undef STRINGIFY1 #undef STRINGIFY +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +// These defines are used to provide compiler-specific flags #if BOUT_HAS_CUDA && defined(__CUDACC__) #define BOUT_HOST_DEVICE __host__ __device__ #define BOUT_HOST __host__ #define BOUT_DEVICE __device__ +#define BOUT_FORCEINLINE __forceinline__ +#elif defined(_MSC_VER) +#define BOUT_HOST_DEVICE +#define BOUT_HOST +#define BOUT_DEVICE +#define BOUT_FORCEINLINE __forceinline +#elif defined(__clang__) || defined(__GNUC__) +#define BOUT_HOST_DEVICE +#define BOUT_HOST +#define BOUT_DEVICE +#define BOUT_FORCEINLINE inline __attribute__((always_inline)) #else #define BOUT_HOST_DEVICE #define BOUT_HOST #define BOUT_DEVICE +#define BOUT_FORCEINLINE inline +#endif + +#if defined(__has_cpp_attribute) && __has_cpp_attribute(assume) >= 202207L +#define BOUT_ASSUME(condition) [[assume(condition)]] +#elif defined(_MSC_VER) +#define BOUT_ASSUME(condition) __assume(condition) +#elif defined(__clang__) +#if __has_builtin(__builtin_assume) +#define BOUT_ASSUME(condition) __builtin_assume(condition) +#else +#define BOUT_ASSUME(condition) ((void)0) +#endif +#elif defined(__GNUC__) +#define BOUT_ASSUME(condition) \ + do { \ + if (!(condition)) { \ + __builtin_unreachable(); \ + } \ + } while (false) +#else +#define BOUT_ASSUME(condition) ((void)0) #endif +// NOLINTEND(cppcoreguidelines-macro-usage) #endif // BOUT_BUILD_OPTIONS_HXX diff --git a/include/bout/coordinates.hxx b/include/bout/coordinates.hxx index fb2fade75d..6d803ab28a 100644 --- a/include/bout/coordinates.hxx +++ b/include/bout/coordinates.hxx @@ -3,16 +3,16 @@ * * ChangeLog * ========= - * + * * 2014-11-10 Ben Dudson * * Created by separating metric from Mesh * - * + * ************************************************************************** - * Copyright 2014 B.D.Dudson + * Copyright 2014-2025 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * - * Contact: Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -33,12 +33,20 @@ #ifndef BOUT_COORDINATES_H #define BOUT_COORDINATES_H -#include "bout/bout_types.hxx" -#include "bout/field2d.hxx" -#include "bout/field3d.hxx" -#include "bout/paralleltransform.hxx" +#include "bout/assert.hxx" +#include "bout/field_data.hxx" +#include +#include +#include +#include +#include +#include + +#include +#include class Mesh; +class YBoundary; /*! * Represents a coordinate system, and associated operators @@ -101,6 +109,124 @@ public: /// Covariant metric tensor FieldMetric g_11, g_22, g_33, g_12, g_13, g_23; + /// get g_22 at the cell faces; + const FieldMetric& g_22_ylow() const; + const FieldMetric& g_22_yhigh() const; + FieldMetric& g_22_ylow(); + FieldMetric& g_22_yhigh(); + // Cell Areas + const FieldMetric& cell_area_xlow() const { + if (!_cell_area_xlow.has_value()) { + _compute_cell_area_x(); + } + ASSERT2(_cell_area_xlow.has_value()); + return *_cell_area_xlow; + } + const FieldMetric& cell_area_xhigh() const { + if (!_cell_area_xhigh.has_value()) { + _compute_cell_area_x(); + } + ASSERT2(_cell_area_xhigh.has_value()); + return *_cell_area_xhigh; + } + const FieldMetric& cell_area_ylow() const { + if (!_cell_area_ylow.has_value()) { + _compute_cell_area_y(); + } + ASSERT2(_cell_area_ylow.has_value()); + return *_cell_area_ylow; + } + const FieldMetric& cell_area_yhigh() const { + if (!_cell_area_yhigh.has_value()) { + _compute_cell_area_y(); + } + ASSERT2(_cell_area_yhigh.has_value()); + return *_cell_area_yhigh; + } + const FieldMetric& cell_area_zlow() const { + if (!_cell_area_zlow.has_value()) { + _compute_cell_area_z(); + } + ASSERT2(_cell_area_zlow.has_value()); + return *_cell_area_zlow; + } + const FieldMetric& cell_area_zhigh() const { + if (!_cell_area_zhigh.has_value()) { + _compute_cell_area_z(); + } + ASSERT2(_cell_area_zhigh.has_value()); + return *_cell_area_zhigh; + } + FieldMetric& cell_area_xlow() { + if (!_cell_area_xlow.has_value()) { + _compute_cell_area_x(); + } + ASSERT2(_cell_area_xlow.has_value()); + return *_cell_area_xlow; + } + FieldMetric& cell_area_xhigh() { + if (!_cell_area_xhigh.has_value()) { + _compute_cell_area_x(); + } + ASSERT2(_cell_area_xhigh.has_value()); + return *_cell_area_xhigh; + } + FieldMetric& cell_area_ylow() { + if (!_cell_area_ylow.has_value()) { + _compute_cell_area_y(); + } + ASSERT2(_cell_area_ylow.has_value()); + return *_cell_area_ylow; + } + FieldMetric& cell_area_yhigh() { + if (!_cell_area_yhigh.has_value()) { + _compute_cell_area_y(); + } + ASSERT2(_cell_area_yhigh.has_value()); + return *_cell_area_yhigh; + } + FieldMetric& cell_area_zlow() { + if (!_cell_area_zlow.has_value()) { + _compute_cell_area_z(); + } + ASSERT2(_cell_area_zlow.has_value()); + return *_cell_area_zlow; + } + FieldMetric& cell_area_zhigh() { + if (!_cell_area_zhigh.has_value()) { + _compute_cell_area_z(); + } + ASSERT2(_cell_area_zhigh.has_value()); + return *_cell_area_zhigh; + } + // Cell Volume + const FieldMetric& cell_volume() const { + if (!_cell_volume.has_value()) { + _compute_cell_volume(); + } + ASSERT2(_cell_volume.has_value()); + return *_cell_volume; + } + FieldMetric& cell_volume() { + if (!_cell_volume.has_value()) { + _compute_cell_volume(); + } + ASSERT2(_cell_volume.has_value()); + return *_cell_volume; + } + +private: + mutable std::optional _g_22_ylow, _g_22_yhigh; + mutable std::optional _cell_area_xlow, _cell_area_xhigh; + mutable std::optional _cell_area_ylow, _cell_area_yhigh; + mutable std::optional _cell_area_zlow, _cell_area_zhigh; + mutable std::optional _cell_volume; + void _compute_cell_area_x() const; + void _compute_cell_area_y() const; + void _compute_cell_area_z() const; + void _compute_cell_volume() const; + +public: /// Christoffel symbol of the second kind (connection coefficients) FieldMetric G1_11, G1_22, G1_33, G1_12, G1_13, G1_23; FieldMetric G2_11, G2_22, G2_33, G2_12, G2_13, G2_23; @@ -132,9 +258,10 @@ public: transform = std::move(pt); } + bool hasParallelTransform() const { return transform != nullptr; } /// Return the parallel transform - ParallelTransform& getParallelTransform() { - ASSERT1(transform != nullptr); + ParallelTransform& getParallelTransform() const { + ASSERT1(hasParallelTransform()); return *transform; } @@ -158,7 +285,7 @@ public: const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); - Field3D DDY(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + Field3D DDY(const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") const; @@ -170,7 +297,7 @@ public: FieldMetric Grad_par(const Field2D& var, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); - Field3D Grad_par(const Field3D& var, CELL_LOC outloc = CELL_DEFAULT, + Field3D Grad_par(const Field3DParallel& var, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); /// Advection along magnetic field V*b.Grad(f) @@ -178,7 +305,7 @@ public: CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); - Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, + Field3D Vpar_Grad_par(const Field3D& v, const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); @@ -186,14 +313,14 @@ public: FieldMetric Div_par(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); - Field3D Div_par(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + Field3D Div_par(const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); // Second derivative along magnetic field FieldMetric Grad2_par2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); - Field3D Grad2_par2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + Field3D Grad2_par2(const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); // Perpendicular Laplacian operator, using only X-Z derivatives // NOTE: This might be better bundled with the Laplacian inversion code @@ -205,13 +332,13 @@ public: // Full parallel Laplacian operator on scalar field // Laplace_par(f) = Div( b (b dot Grad(f)) ) FieldMetric Laplace_par(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT); - Field3D Laplace_par(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT); + Field3D Laplace_par(const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT); // Full Laplacian operator on scalar field FieldMetric Laplace(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& dfdy_boundary_conditions = "free_o3", const std::string& dfdy_dy_region = ""); - Field3D Laplace(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + Field3D Laplace(const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& dfdy_boundary_conditions = "free_o3", const std::string& dfdy_dy_region = ""); @@ -219,9 +346,13 @@ public: // solver Field2D Laplace_perpXY(const Field2D& A, const Field2D& f); + friend std::shared_ptr getYBoundary(Coordinates* coords, YBndryType type); + private: + std::shared_ptr makeYBoundary(YBndryType type) const; int nz; // Size of mesh in Z. This is mesh->ngz-1 Mesh* localmesh; + Options* localoptions; CELL_LOC location; /// Handles calculation of yup and ydown @@ -247,18 +378,8 @@ private: void checkCovariant(); // check that contravariant tensors are positive (if expected) and finite (always) void checkContravariant(); -}; -/* -/// Standard coordinate system for tokamak simulations -class TokamakCoordinates : public Coordinates { -public: - TokamakCoordinates(Mesh *mesh) : Coordinates(mesh) { - - } -private: - + mutable std::array, 3> ybndrys; }; -*/ #endif // BOUT_COORDINATES_H diff --git a/include/bout/coordinates_accessor.hxx b/include/bout/coordinates_accessor.hxx index 532351d57a..2376ab5039 100644 --- a/include/bout/coordinates_accessor.hxx +++ b/include/bout/coordinates_accessor.hxx @@ -31,7 +31,7 @@ /// -> If Coordinates data is changed, the cache should be cleared /// by calling CoordinatesAccessor::clear() struct CoordinatesAccessor { - CoordinatesAccessor() = delete; + CoordinatesAccessor() {} /// Constructor from Coordinates /// Copies data from coords, doesn't modify it diff --git a/include/bout/cyclic_reduction.hxx b/include/bout/cyclic_reduction.hxx index d4c0920910..cf54b63059 100644 --- a/include/bout/cyclic_reduction.hxx +++ b/include/bout/cyclic_reduction.hxx @@ -47,7 +47,6 @@ //#define DIAGNOSE 1 #include "mpi.h" -#include "bout/msg_stack.hxx" #include "bout/utils.hxx" #include @@ -118,7 +117,6 @@ public: /// @param[in] b Diagonal values. Should have size [nsys][N] /// @param[in] c Right diagonal. Should have size [nsys][N] void setCoefs(const Matrix& a, const Matrix& b, const Matrix& c) { - TRACE("CyclicReduce::setCoefs"); int nsys = std::get<0>(a.shape()); @@ -169,7 +167,7 @@ public: /// @param[in] rhs Matrix storing Values of the rhs for each system /// @param[out] x Matrix storing the result for each system void solve(const Matrix& rhs, Matrix& x) { - TRACE("CyclicReduce::solve"); + ASSERT2(static_cast(std::get<0>(rhs.shape())) == Nsys); ASSERT2(static_cast(std::get<0>(x.shape())) == Nsys); ASSERT2(static_cast(std::get<1>(rhs.shape())) == N); diff --git a/include/bout/dcomplex.hxx b/include/bout/dcomplex.hxx index 556f1e72bb..2d3e0c5ca7 100644 --- a/include/bout/dcomplex.hxx +++ b/include/bout/dcomplex.hxx @@ -7,12 +7,12 @@ * * 2015-03-09 Ben Dudson * o Removed, redefined in terms of std::complex - * + * ************************************************************************** * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -27,7 +27,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with BOUT++. If not, see . - * + * */ #ifndef BOUT_DCOMPLEX_H #define BOUT_DCOMPLEX_H diff --git a/include/bout/deriv_store.hxx b/include/bout/deriv_store.hxx index 6dc44c76ad..7c08802cae 100644 --- a/include/bout/deriv_store.hxx +++ b/include/bout/deriv_store.hxx @@ -29,16 +29,18 @@ #ifndef __DERIV_STORE_HXX__ #define __DERIV_STORE_HXX__ +#include #include #include #include +#include #include +#include "bout/field3d.hxx" #include #include #include -#include #include /// Here we have a templated singleton that is used to store DerivativeFunctions @@ -75,21 +77,14 @@ struct DerivativeStore { } /// Report if store has any registered methods - bool isEmpty() const { - AUTO_TRACE(); - return registeredMethods.empty(); - }; + bool isEmpty() const { return registeredMethods.empty(); }; /// Report if store has any registered methods for specific type determined by key - bool isEmpty(std::size_t key) const { - AUTO_TRACE(); - return registeredMethods.count(key) == 0; - } + bool isEmpty(std::size_t key) const { return registeredMethods.count(key) == 0; } /// Report if store has any registered methods for specific type bool isEmpty(DERIV derivType, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); // Get the key auto key = getKey(direction, stagger, toString(derivType)); @@ -100,7 +95,6 @@ struct DerivativeStore { /// specified derivative type, direction and stagger. std::set getAvailableMethods(DERIV derivType, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); // Get the key auto key = getKey(direction, stagger, toString(derivType)); @@ -115,7 +109,6 @@ struct DerivativeStore { /// specified derivative type, direction and stagger. void listAvailableMethods(DERIV derivType, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); // Introductory information output_info << "Available methods for derivative type '"; @@ -134,7 +127,7 @@ struct DerivativeStore { /// depends on the derivType input. void registerDerivative(standardFunc func, DERIV derivType, DIRECTION direction, STAGGER stagger, std::string methodName) { - AUTO_TRACE(); + const auto key = getKey(direction, stagger, methodName); switch (derivType) { @@ -176,7 +169,7 @@ struct DerivativeStore { /// depends on the derivType input. void registerDerivative(upwindFunc func, DERIV derivType, DIRECTION direction, STAGGER stagger, std::string methodName) { - AUTO_TRACE(); + const auto key = getKey(direction, stagger, methodName); switch (derivType) { @@ -210,14 +203,14 @@ struct DerivativeStore { template void registerDerivative(standardFunc func, Direction direction, Stagger stagger, Method method) { - AUTO_TRACE(); + registerDerivative(func, method.meta.derivType, direction.lookup(), stagger.lookup(), method.meta.key); } template void registerDerivative(upwindFunc func, Direction direction, Stagger stagger, Method method) { - AUTO_TRACE(); + registerDerivative(func, method.meta.derivType, direction.lookup(), stagger.lookup(), method.meta.key); } @@ -231,7 +224,6 @@ struct DerivativeStore { STAGGER stagger = STAGGER::None, DERIV derivType = DERIV::Standard) const { - AUTO_TRACE(); const auto realName = nameLookup( name, defaultMethods.at(getKey(direction, stagger, toString(derivType)))); const auto key = getKey(direction, stagger, realName); @@ -262,20 +254,20 @@ struct DerivativeStore { standardFunc getStandard2ndDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return getStandardDerivative(name, direction, stagger, DERIV::StandardSecond); }; standardFunc getStandard4thDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return getStandardDerivative(name, direction, stagger, DERIV::StandardFourth); }; flowFunc getFlowDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None, DERIV derivType = DERIV::Upwind) const { - AUTO_TRACE(); + const auto realName = nameLookup( name, defaultMethods.at(getKey(direction, stagger, toString(derivType)))); const auto key = getKey(direction, stagger, realName); @@ -305,18 +297,17 @@ struct DerivativeStore { upwindFunc getUpwindDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return getFlowDerivative(name, direction, stagger, DERIV::Upwind); }; fluxFunc getFluxDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return getFlowDerivative(name, direction, stagger, DERIV::Flux); }; void initialise(Options* options) { - AUTO_TRACE(); // To replicate the existing behaviour we first search for a section called //"dd?" and if the option isn't in there we search a section called "diff" @@ -490,7 +481,7 @@ private: std::string getMethodName(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return name + " (" + toString(direction) + ", " + toString(stagger) + ")"; }; @@ -506,7 +497,7 @@ private: /// methods with the same function interface in the same map, which /// might be nice. std::size_t getKey(DIRECTION direction, STAGGER stagger, std::string key) const { - AUTO_TRACE(); + // Note this key is indepedent of the field type (and hence the // key is the same for 3D/2D fields) as we have to use different // maps to store the different field types as the signature is @@ -524,7 +515,7 @@ private: /// that can be used to account for run-time choices template std::size_t getKey() const { - AUTO_TRACE(); + // Note this key is indepedent of the field type (and hence the // key is the same for 3D/2D fields) as we have to use different // maps to store the different field types as the signature is @@ -533,4 +524,13 @@ private: } }; +template +auto& getStore() { + if constexpr (std::is_same::value) { + return DerivativeStore::getInstance(); + } else { + return DerivativeStore::getInstance(); + } +} + #endif diff --git a/include/bout/derivs.hxx b/include/bout/derivs.hxx index 1c360bb9cd..a8d9279378 100644 --- a/include/bout/derivs.hxx +++ b/include/bout/derivs.hxx @@ -82,7 +82,7 @@ Coordinates::FieldMetric DDX(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field3D DDY(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, +Field3D DDY(const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); @@ -410,7 +410,7 @@ Coordinates::FieldMetric VDDX(const Field2D& v, const Field2D& f, /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field3D VDDY(const Field3D& v, const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, +Field3D VDDY(const Field3D& v, const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); @@ -533,7 +533,7 @@ Coordinates::FieldMetric FDDX(const Field2D& v, const Field2D& f, /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field3D FDDY(const Field3D& v, const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, +Field3D FDDY(const Field3D& v, const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); diff --git a/include/bout/difops.hxx b/include/bout/difops.hxx index e891dadb60..f16f5acc93 100644 --- a/include/bout/difops.hxx +++ b/include/bout/difops.hxx @@ -1,11 +1,11 @@ /*!****************************************************************************** * \file difops.hxx - * + * * Differential operators * * Changelog: * - * 2009-01 Ben Dudson + * 2009-01 Ben Dudson * * Added two optional parameters which can be put in any order * These determine the method to use (DIFF_METHOD) * and CELL_LOC location of the result. @@ -15,7 +15,7 @@ * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -30,7 +30,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with BOUT++. If not, see . - * + * *******************************************************************************/ #ifndef BOUT_DIFOPS_H @@ -40,7 +40,9 @@ #include "bout/field3d.hxx" #include "bout/bout_types.hxx" -#include "bout/solver.hxx" +#include "bout/coordinates.hxx" + +class Solver; /*! * Parallel derivative (central differencing) in Y @@ -79,7 +81,7 @@ Field3D Grad_parP(const Field3D& apar, const Field3D& f); * \f[ * v\mathbf{b}_0 \cdot \nabla f * \f] - * + * * * @param[in] v The velocity in y direction * @param[in] f The scalar field to be differentiated @@ -173,7 +175,7 @@ inline Field3D Grad2_par2(const Field3D& f, CELL_LOC outloc, DIFF_METHOD method) /*! * Parallel divergence of diffusive flux, K*Grad_par - * + * * \f[ * \nabla \cdot ( \mathbf{b}_0 kY (\mathbf{b}_0 \cdot \nabla) f ) * \f] @@ -197,8 +199,8 @@ Field3D Div_par_K_Grad_par(const Field3D& kY, const Field3D& f, * Perpendicular Laplacian operator * * This version only includes terms in X and Z, dropping - * derivatives in Y. This is the inverse operation to - * the Laplacian inversion class. + * derivatives in Y. This is the inverse operation to + * the Laplacian inversion class. * * For the full perpendicular Laplacian, use Laplace_perp */ @@ -210,7 +212,7 @@ FieldPerp Delp2(const FieldPerp& f, CELL_LOC outloc = CELL_DEFAULT, bool useFFT /*! * Perpendicular Laplacian, keeping y derivatives * - * + * */ Coordinates::FieldMetric Laplace_perp(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, @@ -244,18 +246,18 @@ Field2D Laplace_perpXY(const Field2D& A, const Field2D& f); /*! * Terms of form b0 x Grad(phi) dot Grad(A) - * + * */ Coordinates::FieldMetric b0xGrad_dot_Grad(const Field2D& phi, const Field2D& A, CELL_LOC outloc = CELL_DEFAULT); /*! - * Terms of form + * Terms of form * * \f[ * \mathbf{b}_0 \times \nabla \phi \cdot \nabla A * \f] - * + * * @param[in] phi The scalar potential * @param[in] A The field being advected * @param[in] outloc The cell location where the result is defined. By default the same as A. @@ -271,18 +273,16 @@ Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field3D& A, * Poisson bracket methods */ enum class BRACKET_METHOD { - standard, ///< Use b0xGrad_dot_Grad - simple, ///< Keep only terms in X-Z - arakawa, ///< Arakawa method in X-Z (optimised) - ctu, ///< Corner Transport Upwind (CTU) method. Explicit method only, needs the - /// timestep from the solver - arakawa_old ///< Older version, for regression testing of optimised version. + standard, ///< Use b0xGrad_dot_Grad + simple, ///< Keep only terms in X-Z + arakawa, ///< Arakawa method in X-Z + ctu, ///< Corner Transport Upwind (CTU) method. Explicit method only, needs the + /// timestep from the solver }; constexpr BRACKET_METHOD BRACKET_STD = BRACKET_METHOD::standard; constexpr BRACKET_METHOD BRACKET_SIMPLE = BRACKET_METHOD::simple; constexpr BRACKET_METHOD BRACKET_ARAKAWA = BRACKET_METHOD::arakawa; constexpr BRACKET_METHOD BRACKET_CTU = BRACKET_METHOD::ctu; -constexpr BRACKET_METHOD BRACKET_ARAKAWA_OLD = BRACKET_METHOD::arakawa_old; /*! * Compute advection operator terms, which can be cast as @@ -291,13 +291,13 @@ constexpr BRACKET_METHOD BRACKET_ARAKAWA_OLD = BRACKET_METHOD::arakawa_old; * \f[ * [f, g] = (1/B) \mathbf{b}_0 \times \nabla f \cdot \nabla g * \f] - * + * * @param[in] f The potential * @param[in] g The field being advected * @param[in] method The method to use * @param[in] outloc The cell location where the result is defined. Default is the same as g * @param[in] solver Pointer to the time integration solver - * + * */ Coordinates::FieldMetric bracket(const Field2D& f, const Field2D& g, BRACKET_METHOD method = BRACKET_STD, diff --git a/include/bout/fft.hxx b/include/bout/fft.hxx index fdec8b7bec..b7d36d2166 100644 --- a/include/bout/fft.hxx +++ b/include/bout/fft.hxx @@ -28,10 +28,15 @@ #ifndef BOUT_FFT_H #define BOUT_FFT_H -#include "bout/dcomplex.hxx" +#include "bout/build_defines.hxx" + #include #include +#include + +#include +class Mesh; class Options; BOUT_ENUM_CLASS(FFT_MEASUREMENT_FLAG, estimate, measure, exhaustive); @@ -111,6 +116,16 @@ Array rfft(const Array& in); /// Expects that `in.size() == (length / 2) + 1` Array irfft(const Array& in, int length); +/// Check simulation is using 1 processor in Z, throw exception if not +/// +/// Generally, FFTs must be done over the full Z domain. Currently, most +/// methods using FFTs don't handle parallelising in Z +#if BOUT_CHECK_LEVEL > 0 +void assertZSerial(const Mesh& mesh, std::string_view name); +#else +inline void assertZSerial([[maybe_unused]] const Mesh& mesh, + [[maybe_unused]] std::string_view name) {} +#endif } // namespace fft } // namespace bout diff --git a/include/bout/field.hxx b/include/bout/field.hxx index 026b2838a8..b39a82eb0b 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -3,10 +3,10 @@ * \brief field base class definition for differencing methods * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * - * Contact: Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -31,19 +31,21 @@ class Field; #include #include +#include #include #include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" #include "bout/field_data.hxx" -#include "bout/msg_stack.hxx" #include "bout/region.hxx" #include "bout/traits.hxx" #include "bout/utils.hxx" #include #include +#include "bout/fieldops.hxx" + class Mesh; /// Base class for scalar fields @@ -79,6 +81,8 @@ public: std::string name; + bool isFci() const; + #if CHECK > 0 // Routines to test guard/boundary cells set @@ -125,6 +129,18 @@ public: swap(first.directions, second.directions); } + /// Dummy functions to increase portability + virtual void setRegion([[maybe_unused]] size_t regionID) {} + virtual void setRegion([[maybe_unused]] std::optional regionID) {} + virtual void setRegion([[maybe_unused]] const std::string& region_name) {} + virtual void resetRegion() {} + virtual std::optional getRegionID() const { return {}; } + virtual bool hasParallelSlices() const { return true; } + virtual void calcParallelSlices() {} + virtual void splitParallelSlices() {} + virtual void clearParallelSlices() {} + virtual size_t numberParallelSlices() const { return 0; } + private: /// Labels for the type of coordinate system this field is defined over DirectionTypes directions{YDirectionType::Standard, ZDirectionType::Standard}; @@ -167,8 +183,30 @@ inline bool areFieldsCompatible(const Field& field1, const Field& field2) { #field2, toString((field2).getDirections())); \ } +#define ASSERT1_EXPR_COMPATIBLE(expr1, expr2) \ + if ((expr1).getLocation() != (expr2).getLocation()) { \ + throw BoutException("Error in {:s}:{:d}\nFields at different position:" \ + "`{:s}` at {:s}, `{:s}` at {:s}", \ + __FILE__, __LINE__, #expr1, toString((expr1).getLocation()), \ + #expr2, toString((expr2).getLocation())); \ + } \ + if ((expr1).getMesh() != (expr2).getMesh()) { \ + throw BoutException("Error in {:s}:{:d}\nFields are on different Meshes:" \ + "`{:s}` at {:p}, `{:s}` at {:p}", \ + __FILE__, __LINE__, #expr1, \ + static_cast((expr1).getMesh()), #expr2, \ + static_cast((expr2).getMesh())); \ + } \ + if (!areDirectionsCompatible((expr1).getDirections(), (expr2).getDirections())) { \ + throw BoutException("Error in {:s}:{:d}\nFields at different directions:" \ + "`{:s}` at {:s}, `{:s}` at {:s}", \ + __FILE__, __LINE__, #expr1, toString((expr1).getDirections()), \ + #expr2, toString((expr2).getDirections())); \ + } + #else #define ASSERT1_FIELDS_COMPATIBLE(field1, field2) ; +#define ASSERT1_EXPR_COMPATIBLE(expr1, expr2) ; #endif /// Return an empty shell field of some type derived from Field, with metadata @@ -176,7 +214,8 @@ inline bool areFieldsCompatible(const Field& field1, const Field& field2) { template inline T emptyFrom(const T& f) { static_assert(bout::utils::is_Field_v, "emptyFrom only works on Fields"); - return T(f.getMesh(), f.getLocation(), {f.getDirectionY(), f.getDirectionZ()}) + return T(f.getMesh(), f.getLocation(), + DirectionTypes{f.getDirectionY(), f.getDirectionZ()}, f.getRegionID()) .allocate(); } @@ -237,7 +276,6 @@ namespace bout { template inline void checkFinite(const T& f, const std::string& name = "field", const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); if (!f.isAllocated()) { throw BoutException("{:s} is not allocated", name); @@ -261,7 +299,6 @@ inline void checkFinite(const T& f, const std::string& name = "field", template inline void checkPositive(const T& f, const std::string& name = "field", const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); if (!f.isAllocated()) { throw BoutException("{:s} is not allocated", name); @@ -283,6 +320,7 @@ inline void checkPositive(const T& f, const std::string& name = "field", template inline T toFieldAligned(const T& f, const std::string& region = "RGN_ALL") { static_assert(bout::utils::is_Field_v, "toFieldAligned only works on Fields"); + ASSERT3(f.getCoordinates() != nullptr); return f.getCoordinates()->getParallelTransform().toFieldAligned(f, region); } @@ -290,6 +328,7 @@ inline T toFieldAligned(const T& f, const std::string& region = "RGN_ALL") { template inline T fromFieldAligned(const T& f, const std::string& region = "RGN_ALL") { static_assert(bout::utils::is_Field_v, "fromFieldAligned only works on Fields"); + ASSERT3(f.getCoordinates() != nullptr); return f.getCoordinates()->getParallelTransform().fromFieldAligned(f, region); } @@ -305,7 +344,6 @@ inline T fromFieldAligned(const T& f, const std::string& region = "RGN_ALL") { template > inline BoutReal min(const T& f, bool allpe = false, const std::string& rgn = "RGN_NOBNDRY") { - AUTO_TRACE(); checkData(f); @@ -327,6 +365,24 @@ inline BoutReal min(const T& f, bool allpe = false, return result; } +template +inline BoutReal min(const BinaryExpr& f, bool allpe = false, + const std::string& rgn = "RGN_NOBNDRY") { + const auto& region = f.getMesh()->template getRegion(rgn); + const auto reduction_view = + makeReductionView(static_cast::View>(f), + region.getLinearIndices()); + BoutReal result = + bout::reduce::Min::finalize(reduceExpr(reduction_view)); + + if (allpe) { + BoutReal localresult = result; + MPI_Allreduce(&localresult, &result, 1, MPI_DOUBLE, MPI_MIN, BoutComm::get()); + } + + return result; +} + /// Returns true if all elements of \p f over \p region are equal. By /// default only checks the local processor, use \p allpe to check /// globally @@ -390,7 +446,6 @@ inline BoutReal getUniform(const T& f, [[maybe_unused]] bool allpe = false, template > inline BoutReal max(const T& f, bool allpe = false, const std::string& rgn = "RGN_NOBNDRY") { - AUTO_TRACE(); checkData(f); @@ -412,6 +467,24 @@ inline BoutReal max(const T& f, bool allpe = false, return result; } +template +inline BoutReal max(const BinaryExpr& f, bool allpe = false, + const std::string& rgn = "RGN_NOBNDRY") { + const auto& region = f.getMesh()->template getRegion(rgn); + const auto reduction_view = + makeReductionView(static_cast::View>(f), + region.getLinearIndices()); + BoutReal result = + bout::reduce::Max::finalize(reduceExpr(reduction_view)); + + if (allpe) { + BoutReal localresult = result; + MPI_Allreduce(&localresult, &result, 1, MPI_DOUBLE, MPI_MAX, BoutComm::get()); + } + + return result; +} + /// Mean of \p f, excluding the boundary/guard cells by default (can /// be changed with \p rgn argument). /// @@ -424,7 +497,6 @@ inline BoutReal max(const T& f, bool allpe = false, template > inline BoutReal mean(const T& f, bool allpe = false, const std::string& rgn = "RGN_NOBNDRY") { - AUTO_TRACE(); checkData(f); @@ -448,6 +520,25 @@ inline BoutReal mean(const T& f, bool allpe = false, return result / static_cast(count); } +template +inline BoutReal mean(const BinaryExpr& f, bool allpe = false, + const std::string& rgn = "RGN_NOBNDRY") { + const auto& region = f.getMesh()->template getRegion(rgn); + const auto reduction_view = + makeReductionView(static_cast::View>(f), + region.getLinearIndices()); + auto state = reduceExpr(reduction_view); + + if (allpe) { + BoutReal localsum = state.sum; + int localcount = state.count; + MPI_Allreduce(&localsum, &state.sum, 1, MPI_DOUBLE, MPI_SUM, BoutComm::get()); + MPI_Allreduce(&localcount, &state.count, 1, MPI_INT, MPI_SUM, BoutComm::get()); + } + + return bout::reduce::Mean::finalize(state); +} + /// Exponent: pow(lhs, lhs) is \p lhs raised to the power of \p rhs /// /// This loops over the entire domain, including guard/boundary cells by @@ -455,7 +546,6 @@ inline BoutReal mean(const T& f, bool allpe = false, /// If CHECK >= 3 then the result will be checked for non-finite numbers template > T pow(const T& lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); ASSERT1(areFieldsCompatible(lhs, rhs)); @@ -469,7 +559,6 @@ T pow(const T& lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { template > T pow(const T& lhs, BoutReal rhs, const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); // Check if the inputs are allocated checkData(lhs); @@ -485,7 +574,6 @@ T pow(const T& lhs, BoutReal rhs, const std::string& rgn = "RGN_ALL") { template > T pow(BoutReal lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); // Check if the inputs are allocated checkData(lhs); @@ -516,23 +604,147 @@ T pow(BoutReal lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { * result for non-finite numbers * */ +class Field3DParallel; +class FieldPerp; + +namespace bout::detail { +template +std::optional getPerpYIndex(const T& value) { + if constexpr (std::is_same_v, ::FieldPerp>) { + return value.getIndex(); + } else { + return std::nullopt; + } +} + +template +std::optional getPerpYIndex(const BinaryExpr& expr) { + if constexpr (std::is_same_v) { + return expr.getIndex(); + } else { + return std::nullopt; + } +} +} // namespace bout::detail + #ifdef FIELD_FUNC #error This macro has already been defined #else -#define FIELD_FUNC(name, func) \ - template > \ - inline T name(const T& f, const std::string& rgn = "RGN_ALL") { \ - AUTO_TRACE(); \ - /* Check if the input is allocated */ \ - checkData(f); \ - /* Define and allocate the output result */ \ - T result{emptyFrom(f)}; \ - BOUT_FOR(d, result.getRegion(rgn)) { result[d] = func(f[d]); } \ - checkData(result); \ - return result; \ +#define FIELD_FUNC(name, func) \ + namespace bout::op { \ + struct name { \ + template \ + BOUT_HOST_DEVICE BoutReal operator()(int idx, const LView& L, const RView&) const { \ + return func(L(idx)); \ + } \ + }; \ + }; \ + template > \ + inline auto name(const T& f, const std::string& rgn = "RGN_ALL") { \ + if constexpr (std::is_same_v) { \ + /* Check if the input is allocated */ \ + checkData(f); \ + /* Define and allocate the output result */ \ + T result{emptyFrom(f)}; \ + BOUT_FOR(d, result.getRegion(rgn)) { result[d] = func(f[d]); } \ + for (int i = 0; i < f.numberParallelSlices(); ++i) { \ + result.yup(i) = func(f.yup(i)); \ + result.ydown(i) = func(f.ydown(i)); \ + } \ + result.name = std::string(#name "(") + f.name + std::string(")"); \ + checkData(result); \ + return result; \ + } else { \ + return BinaryExpr{static_cast(f), \ + static_cast(f), \ + bout::op::name{}, \ + f.getMesh(), \ + f.getLocation(), \ + f.getDirections(), \ + std::nullopt, \ + f.getRegion(rgn), \ + bout::detail::getPerpYIndex(f)}; \ + } \ + } \ + template \ + inline auto name(const BinaryExpr& f) { \ + return BinaryExpr, BinaryExpr, \ + bout::op::name>{ \ + static_cast::View>(f), \ + static_cast::View>(f), \ + bout::op::name{}, \ + f.getMesh(), \ + f.getLocation(), \ + f.getDirections(), \ + f.getRegionID(), \ + f.indices, \ + bout::detail::getPerpYIndex(f)}; \ + } \ + template \ + inline auto name(const BinaryExpr& f, const std::string& rgn) { \ + return name(ResT{f}, rgn); \ } #endif +namespace bout::op { +struct Square { + template + BOUT_HOST_DEVICE BoutReal operator()(int idx, const LView& L, const RView&) const { + const BoutReal value = L(idx); + return ::SQ(value); + } +}; +}; // namespace bout::op + +template > +inline auto SQ(const T& f, const std::string& rgn = "RGN_ALL") { + if constexpr (std::is_same_v) { + checkData(f); + T result{emptyFrom(f)}; + if (f.hasParallelSlices() and !result.hasParallelSlices()) { + result.splitParallelSlices(); + } + BOUT_FOR(d, result.getRegion(rgn)) { result[d] = ::SQ(f[d]); } + for (size_t i = 0; i < f.numberParallelSlices(); ++i) { + result.yup(i) = SQ(f.yup(i), rgn); + result.ydown(i) = SQ(f.ydown(i), rgn); + } + result.name = std::string("SQ(") + f.name + std::string(")"); + checkData(result); + return result; + } else { + return BinaryExpr{static_cast(f), + static_cast(f), + bout::op::Square{}, + f.getMesh(), + f.getLocation(), + f.getDirections(), + std::nullopt, + f.getRegion(rgn), + bout::detail::getPerpYIndex(f)}; + } +} + +template +inline auto SQ(const BinaryExpr& f) { + return BinaryExpr, BinaryExpr, + bout::op::Square>{ + static_cast::View>(f), + static_cast::View>(f), + bout::op::Square{}, + f.getMesh(), + f.getLocation(), + f.getDirections(), + f.getRegionID(), + f.indices, + bout::detail::getPerpYIndex(f)}; +} + +template +inline auto SQ(const BinaryExpr& f, const std::string& rgn) { + return SQ(ResT{f}, rgn); +} + /// Square root of \p f over region \p rgn /// /// This loops over the entire domain, including guard/boundary cells by @@ -630,7 +842,6 @@ FIELD_FUNC(tanh, ::tanh) /// default (can be changed using the \p rgn argument template > inline bool finite(const T& f, const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); if (!f.isAllocated()) { return false; @@ -654,6 +865,8 @@ T copy(const T& f) { return result; } +class Field3DParallel; + /// Apply a floor value \p f to a field \p var. Any value lower than /// the floor is set to the floor. /// @@ -670,7 +883,36 @@ inline T floor(const T& var, BoutReal f, const std::string& rgn = "RGN_ALL") { result[d] = f; } } - + if constexpr (std::is_same_v) { + if (var.hasParallelSlices()) { + for (size_t i = 0; i < result.numberParallelSlices(); ++i) { + if (result.yup(i).isAllocated()) { + BOUT_FOR(d, result.yup(i).getRegion(rgn)) { + if (result.yup(i)[d] < f) { + result.yup(i)[d] = f; + } + } + } else { + if (result.isFci()) { + throw BoutException("Expected parallel slice to be allocated"); + } + } + if (result.ydown(i).isAllocated()) { + BOUT_FOR(d, result.ydown(i).getRegion(rgn)) { + if (result.ydown(i)[d] < f) { + result.ydown(i)[d] = f; + } + } + } else { + if (result.isFci()) { + throw BoutException("Expected parallel slice to be allocated"); + } + } + } + } + } else { + result.clearParallelSlices(); + } return result; } diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index 92658f1bbf..0a670fa0d0 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -4,9 +4,9 @@ * \brief Definition of 2D scalar field class * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -24,6 +24,8 @@ * along with BOUT++. If not, see . * */ +#include "bout/utils.hxx" +#include class Field2D; #pragma once @@ -31,12 +33,19 @@ class Field2D; #define BOUT_FIELD2D_H #include "bout/array.hxx" -#include "bout/build_config.hxx" +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/build_defines.hxx" #include "bout/field.hxx" #include "bout/field_data.hxx" #include "bout/fieldperp.hxx" #include "bout/region.hxx" -#include "bout/unused.hxx" + +#include +#include +#include + +#include "bout/fieldops.hxx" #if BOUT_HAS_RAJA #include "RAJA/RAJA.hpp" // using RAJA lib @@ -45,6 +54,15 @@ class Field2D; class Field3D; class Mesh; +template +struct is_expr_field2d> + : std::integral_constant> + && is_expr_field2d_v>) + || (is_expr_constant_v> + && is_expr_field2d_v>) + || (is_expr_field2d_v> + && is_expr_constant_v>)> {}; + /*! * \brief 2D X-Y scalar fields * @@ -66,7 +84,8 @@ public: */ Field2D(Mesh* localmesh = nullptr, CELL_LOC location_in = CELL_CENTRE, DirectionTypes directions_in = {YDirectionType::Standard, - ZDirectionType::Average}); + ZDirectionType::Average}, + std::optional regionID = {}); /*! * Copy constructor. After this both fields @@ -91,6 +110,14 @@ public: DirectionTypes directions_in = {YDirectionType::Standard, ZDirectionType::Average}); + template < + typename ResT, typename L, typename R, typename Func, + typename = std::enable_if_t<(is_expr_field2d_v && is_expr_field2d_v) + || (is_expr_constant_v && is_expr_field2d_v) + || (is_expr_field2d_v && is_expr_constant_v)>> + Field2D(const BinaryExpr& expr) + : Field2D(evaluateBinaryExpr(expr), expr.getMesh(), expr.getLocation(), + expr.getDirections()) {} /*! * Destructor */ @@ -133,21 +160,14 @@ public: return *this; } - /// Check if this field has yup and ydown fields - bool hasParallelSlices() const { return true; } + Field2D& yup([[maybe_unused]] size_t index = 0) { return *this; } + const Field2D& yup([[maybe_unused]] size_t index = 0) const { return *this; } - Field2D& yup(std::vector::size_type UNUSED(index) = 0) { return *this; } - const Field2D& yup(std::vector::size_type UNUSED(index) = 0) const { - return *this; - } + Field2D& ydown([[maybe_unused]] size_t index = 0) { return *this; } + const Field2D& ydown([[maybe_unused]] size_t index = 0) const { return *this; } - Field2D& ydown(std::vector::size_type UNUSED(index) = 0) { return *this; } - const Field2D& ydown(std::vector::size_type UNUSED(index) = 0) const { - return *this; - } - - Field2D& ynext(int UNUSED(dir)) { return *this; } - const Field2D& ynext(int UNUSED(dir)) const { return *this; } + Field2D& ynext([[maybe_unused]] int dir) { return *this; } + const Field2D& ynext([[maybe_unused]] int dir) const { return *this; } // Operators @@ -166,6 +186,21 @@ public: */ Field2D& operator=(BoutReal rhs); + template + std::enable_if_t, Field2D&> + operator=(const BinaryExpr& expr) { + if (!isAllocated() || getMesh() != expr.getMesh()) { + *this = Field2D{expr}; + return *this; + } + + setLocation(expr.getLocation()); + setDirections(expr.getDirections()); + allocate(); + expr.evaluate(&data[0]); + return *this; + } + ///////////////////////////////////////////////////////// // Data access @@ -209,7 +244,7 @@ public: } #endif - return data[jx * ny + jy]; + return data[(jx * ny) + jy]; } inline const BoutReal& operator()(int jx, int jy) const { #if CHECK > 2 && !BOUT_HAS_CUDA @@ -223,38 +258,49 @@ public: } #endif - return data[jx * ny + jy]; + return data[(jx * ny) + jy]; } /*! * DIrect access to underlying array. This version is for compatibility * with Field3D objects */ - BoutReal& operator()(int jx, int jy, int UNUSED(jz)) { return operator()(jx, jy); } - const BoutReal& operator()(int jx, int jy, int UNUSED(jz)) const { + BoutReal& operator()(int jx, int jy, [[maybe_unused]] int jz) { + return operator()(jx, jy); + } + const BoutReal& operator()(int jx, int jy, [[maybe_unused]] int jz) const { return operator()(jx, jy); } - /// In-place addition. Copy-on-write used if data is shared + Field2D& operator*=(const Field2D& rhs); + Field2D& operator/=(const Field2D& rhs); Field2D& operator+=(const Field2D& rhs); - /// In-place addition. Copy-on-write used if data is shared - Field2D& operator+=(BoutReal rhs); - /// In-place subtraction. Copy-on-write used if data is shared Field2D& operator-=(const Field2D& rhs); - /// In-place subtraction. Copy-on-write used if data is shared - Field2D& operator-=(BoutReal rhs); - /// In-place multiplication. Copy-on-write used if data is shared - Field2D& operator*=(const Field2D& rhs); - /// In-place multiplication. Copy-on-write used if data is shared Field2D& operator*=(BoutReal rhs); - /// In-place division. Copy-on-write used if data is shared - Field2D& operator/=(const Field2D& rhs); - /// In-place division. Copy-on-write used if data is shared Field2D& operator/=(BoutReal rhs); + Field2D& operator+=(BoutReal rhs); + Field2D& operator-=(BoutReal rhs); - // FieldData virtual functions +#define FIELD2D_OP_EQUALS(OP_SYM) \ + template \ + std::enable_if_t || is_expr_constant_v, Field2D&> \ + operator OP_SYM## = (R rhs) { \ + if (data.unique()) { \ + auto expr = (*this)OP_SYM rhs; \ + expr.evaluate(&data[0]); \ + } else { \ + (*this) = (*this)OP_SYM rhs; \ + } \ + return *this; \ + } - bool is3D() const override { return false; } + FIELD2D_OP_EQUALS(+) + FIELD2D_OP_EQUALS(-) + FIELD2D_OP_EQUALS(*) + FIELD2D_OP_EQUALS(/) + + // FieldData virtual functions + FieldType field_type() const override { return FieldType::field2d; } #if CHECK > 0 void doneComms() override { bndry_xin = bndry_xout = bndry_yup = bndry_ydown = true; } @@ -272,11 +318,50 @@ public: void applyTDerivBoundary() override; void setBoundaryTo(const Field2D& f2d); ///< Copy the boundary region + void swapData(Field2D& other); friend void swap(Field2D& first, Field2D& second) noexcept; - int size() const override { return nx * ny; }; + int size() const override { return nx * ny; } + + /// Return a field that can be used to perform parallel (Y) derivatives. + /// This is used to write generic FA/FCI code. + Field2D& asField3DParallel() { return *this; } + const Field2D& asField3DParallel() const { return *this; } + + struct View { + BoutReal* data; + int mul = 1; + int div = 1; + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx) const { + return data[(idx * mul / div)]; + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal& operator[](int idx) const { + return data[(idx * mul) / div]; + } + + View& setScale(int mul, int div) { + this->mul = mul; + this->div = div; + return *this; + } + }; + operator View() { return View{&data[0]}; } + operator View() const { return View{const_cast(&data[0])}; } + + BOUT_DEVICE inline BoutReal operator()(int i) { return View()(i); } + BOUT_DEVICE inline BoutReal operator()(int i) const { return View()(i); } private: + template + static Array evaluateBinaryExpr(const BinaryExpr& expr) { + const auto* mesh = expr.getMesh(); + ASSERT1(mesh != nullptr); + + Array data{mesh->LocalNx * mesh->LocalNy}; + expr.evaluate(&data[0]); + return data; + } + /// Internal data array. Handles allocation/freeing of memory Array data; @@ -288,42 +373,192 @@ private: }; // Non-member overloaded operators +FieldPerp operator+(const Field2D& lhs, const FieldPerp& rhs); +FieldPerp operator-(const Field2D& lhs, const FieldPerp& rhs); +FieldPerp operator*(const Field2D& lhs, const FieldPerp& rhs); +FieldPerp operator/(const Field2D& lhs, const FieldPerp& rhs); + +#define FIELD2D_FIELD2D_FIELD2D_OP(OP_SYM, OP_TYPE) \ + template \ + std::enable_if_t && is_expr_field2d_v, \ + BinaryExpr> \ + operator OP_SYM(const L& lhs, const R& rhs) { \ + return BinaryExpr{ \ + static_cast(lhs), \ + static_cast(rhs), \ + bout::op::OP_TYPE{}, \ + lhs.getMesh(), \ + lhs.getLocation(), \ + lhs.getDirections(), \ + std::nullopt, \ + lhs.getMesh()->getRegion2D("RGN_ALL")}; \ + } -Field2D operator+(const Field2D& lhs, const Field2D& rhs); -Field2D operator-(const Field2D& lhs, const Field2D& rhs); -Field2D operator*(const Field2D& lhs, const Field2D& rhs); -Field2D operator/(const Field2D& lhs, const Field2D& rhs); +FIELD2D_FIELD2D_FIELD2D_OP(+, Add) +FIELD2D_FIELD2D_FIELD2D_OP(-, Sub) +FIELD2D_FIELD2D_FIELD2D_OP(*, Mul) +FIELD2D_FIELD2D_FIELD2D_OP(/, Div) + +#define FIELD3D_FIELD2D_FIELD3D_OP(OP_SYM, OP_TYPE) \ + template \ + std::enable_if_t && is_expr_field3d_v, \ + BinaryExpr> \ + operator OP_SYM(const L& lhs, const R& rhs) { \ + ASSERT1_EXPR_COMPATIBLE(lhs, rhs); \ + auto regionID = rhs.getRegionID(); \ + int mesh_nz = rhs.getMesh()->LocalNz; \ + return BinaryExpr{ \ + static_cast(lhs).setScale(1, mesh_nz), \ + static_cast(rhs), \ + bout::op::OP_TYPE{}, \ + rhs.getMesh(), \ + rhs.getLocation(), \ + rhs.getDirections(), \ + regionID, \ + rhs.getMesh()->getRegion("RGN_ALL")}; \ + } -Field3D operator+(const Field2D& lhs, const Field3D& rhs); -Field3D operator-(const Field2D& lhs, const Field3D& rhs); -Field3D operator*(const Field2D& lhs, const Field3D& rhs); -Field3D operator/(const Field2D& lhs, const Field3D& rhs); +FIELD3D_FIELD2D_FIELD3D_OP(+, Add) +FIELD3D_FIELD2D_FIELD3D_OP(-, Sub) +FIELD3D_FIELD2D_FIELD3D_OP(*, Mul) +FIELD3D_FIELD2D_FIELD3D_OP(/, Div) + +#define FIELD2D_FIELD2D_BOUTREAL_OP(OP_SYM, OP_TYPE) \ + template \ + std::enable_if_t && is_expr_constant_v, \ + BinaryExpr, bout::op::OP_TYPE>> \ + operator OP_SYM(const L& lhs, R rhs) { \ + return BinaryExpr, bout::op::OP_TYPE>{ \ + static_cast(lhs), \ + static_cast::View>(rhs), \ + bout::op::OP_TYPE{}, \ + lhs.getMesh(), \ + lhs.getLocation(), \ + lhs.getDirections(), \ + std::nullopt, \ + lhs.getMesh()->getRegion2D("RGN_ALL")}; \ + } -Field2D operator+(const Field2D& lhs, BoutReal rhs); -Field2D operator-(const Field2D& lhs, BoutReal rhs); -Field2D operator*(const Field2D& lhs, BoutReal rhs); -Field2D operator/(const Field2D& lhs, BoutReal rhs); +FIELD2D_FIELD2D_BOUTREAL_OP(+, Add) +FIELD2D_FIELD2D_BOUTREAL_OP(-, Sub) +FIELD2D_FIELD2D_BOUTREAL_OP(*, Mul) +FIELD2D_FIELD2D_BOUTREAL_OP(/, Div) + +#define FIELD2D_BOUTREAL_FIELD2D_OP(OP_SYM, OP_TYPE) \ + template \ + std::enable_if_t && is_expr_field2d_v, \ + BinaryExpr, R, bout::op::OP_TYPE>> \ + operator OP_SYM(L lhs, const R& rhs) { \ + return BinaryExpr, R, bout::op::OP_TYPE>{ \ + static_cast::View>(lhs), \ + static_cast(rhs), \ + bout::op::OP_TYPE{}, \ + rhs.getMesh(), \ + rhs.getLocation(), \ + rhs.getDirections(), \ + std::nullopt, \ + rhs.getMesh()->getRegion2D("RGN_ALL")}; \ + } -Field2D operator+(BoutReal lhs, const Field2D& rhs); -Field2D operator-(BoutReal lhs, const Field2D& rhs); -Field2D operator*(BoutReal lhs, const Field2D& rhs); -Field2D operator/(BoutReal lhs, const Field2D& rhs); +FIELD2D_BOUTREAL_FIELD2D_OP(+, Add) +FIELD2D_BOUTREAL_FIELD2D_OP(-, Sub) +FIELD2D_BOUTREAL_FIELD2D_OP(*, Mul) +FIELD2D_BOUTREAL_FIELD2D_OP(/, Div) + +template +std::enable_if_t && is_expr_field2d_v, + BinaryExpr> +if_else(bool condition, const L& lhs, const R& rhs) { + return BinaryExpr{ + static_cast(lhs), + static_cast(rhs), + bout::op::IfElse{condition}, + lhs.getMesh(), + lhs.getLocation(), + lhs.getDirections(), + std::nullopt, + lhs.getMesh()->getRegion2D("RGN_ALL")}; +} + +template +std::enable_if_t && is_expr_field3d_v, + BinaryExpr> +if_else(bool condition, const L& lhs, const R& rhs) { + ASSERT1_EXPR_COMPATIBLE(lhs, rhs); + auto regionID = rhs.getRegionID(); + int mesh_nz = rhs.getMesh()->LocalNz; + return BinaryExpr{ + static_cast(lhs).setScale(1, mesh_nz), + static_cast(rhs), + bout::op::IfElse{condition}, + rhs.getMesh(), + rhs.getLocation(), + rhs.getDirections(), + regionID, + rhs.getMesh()->getRegion("RGN_ALL")}; +} + +template +std::enable_if_t && is_expr_constant_v, + BinaryExpr, bout::op::IfElse>> +if_else(bool condition, const L& lhs, R rhs) { + return BinaryExpr, bout::op::IfElse>{ + static_cast(lhs), + static_cast::View>(rhs), + bout::op::IfElse{condition}, + lhs.getMesh(), + lhs.getLocation(), + lhs.getDirections(), + std::nullopt, + lhs.getMesh()->getRegion2D("RGN_ALL")}; +} + +template +std::enable_if_t && is_expr_field2d_v, + BinaryExpr, R, bout::op::IfElse>> +if_else(bool condition, L lhs, const R& rhs) { + return BinaryExpr, R, bout::op::IfElse>{ + static_cast::View>(lhs), + static_cast(rhs), + bout::op::IfElse{condition}, + rhs.getMesh(), + rhs.getLocation(), + rhs.getDirections(), + std::nullopt, + rhs.getMesh()->getRegion2D("RGN_ALL")}; +} + +template || is_expr_field3d_v>> +auto if_else_zero(bool condition, const L& lhs) { + return if_else(condition, lhs, 0.0); +} /*! * Unary minus. Returns the negative of given field, * iterates over whole domain including guard/boundary cells. */ -Field2D operator-(const Field2D& f); +inline auto operator-(const Field2D& f) { + return BinaryExpr, Field2D, bout::op::Mul>{ + static_cast::View>(-1.0), + static_cast(f), + bout::op::Mul{}, + f.getMesh(), + f.getLocation(), + f.getDirections(), + std::nullopt, + f.getRegion("RGN_ALL")}; +} // Non-member functions inline Field2D toFieldAligned(const Field2D& f, - const std::string& UNUSED(region) = "RGN_ALL") { + [[maybe_unused]] const std::string& region = "RGN_ALL") { return f; } inline Field2D fromFieldAligned(const Field2D& f, - const std::string& UNUSED(region) = "RGN_ALL") { + [[maybe_unused]] const std::string& region = "RGN_ALL") { return f; } @@ -334,15 +569,15 @@ inline Field2D fromFieldAligned(const Field2D& f, /// default (can be changed using the \p rgn argument void checkData(const Field2D& f, const std::string& region = "RGN_NOBNDRY"); #else -inline void checkData(const Field2D& UNUSED(f), - std::string UNUSED(region) = "RGN_NOBNDRY") {} +inline void checkData([[maybe_unused]] const Field2D& f, + [[maybe_unused]] std::string region = "RGN_NOBNDRY") {} #endif /// Force guard cells of passed field \p var to NaN #if CHECK > 2 void invalidateGuards(Field2D& var); #else -inline void invalidateGuards(Field2D& UNUSED(var)) {} +inline void invalidateGuards([[maybe_unused]] Field2D& var) {} #endif /// Average in the Z direction @@ -358,7 +593,7 @@ inline Field2D& ddt(Field2D& f) { return *(f.timeDeriv()); } /// toString template specialisation /// Defined in utils.hxx template <> -inline std::string toString<>(const Field2D& UNUSED(val)) { +inline std::string toString<>([[maybe_unused]] const Field2D& val) { return ""; } diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index a75e38df36..6901b34087 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -1,8 +1,8 @@ /************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * - * Contact: Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -20,6 +20,8 @@ * **************************************************************************/ +#include "bout/utils.hxx" +#include class Field3D; #pragma once @@ -29,15 +31,28 @@ class Field3D; #include "bout/array.hxx" #include "bout/assert.hxx" #include "bout/bout_types.hxx" +#include "bout/build_config.hxx" #include "bout/field.hxx" #include "bout/field2d.hxx" +#include "bout/field_data.hxx" #include "bout/fieldperp.hxx" #include "bout/region.hxx" +#include "bout/traits.hxx" +#include +#include #include +#include +#include +#include +#include #include class Mesh; +class Options; +class Field3DParallel; + +#include "bout/fieldops.hxx" /// Class for 3D X-Y-Z scalar fields /*! @@ -166,7 +181,8 @@ public: */ Field3D(Mesh* localmesh = nullptr, CELL_LOC location_in = CELL_CENTRE, DirectionTypes directions_in = {YDirectionType::Standard, - ZDirectionType::Standard}); + ZDirectionType::Standard}, + std::optional regionID = {}); /*! * Copy constructor @@ -183,8 +199,15 @@ public: Field3D(Array data, Mesh* localmesh, CELL_LOC location = CELL_CENTRE, DirectionTypes directions_in = {YDirectionType::Standard, ZDirectionType::Standard}); + template || is_expr_field3d_v>> + Field3D(const BinaryExpr& expr) + : Field3D(evaluateBinaryExpr(expr), expr.getMesh(), expr.getLocation(), + expr.getDirections()) { + setRegion(expr.getRegionID()); + } /// Destructor - ~Field3D() override; + ~Field3D() override { delete deriv; } /// Data type stored in this field using value_type = BoutReal; @@ -234,15 +257,15 @@ public: * Ensure that this field has separate fields * for yup and ydown. */ - void splitParallelSlices(); + void splitParallelSlices() override; /*! * Clear the parallel slices, yup and ydown */ - void clearParallelSlices(); + void clearParallelSlices() override; /// Check if this field has yup and ydown fields - bool hasParallelSlices() const { + bool hasParallelSlices() const override { #if CHECK > 2 if (yup_fields.size() != ydown_fields.size()) { throw BoutException( @@ -257,26 +280,33 @@ public: #endif } + size_t numberParallelSlices() const override { +#if CHECK > 0 + hasParallelSlices(); +#endif + return yup_fields.size(); + } + /// Check if this field has yup and ydown fields /// Return reference to yup field - Field3D& yup(std::vector::size_type index = 0) { + Field3D& yup(size_t index = 0) { ASSERT2(index < yup_fields.size()); return yup_fields[index]; } /// Return const reference to yup field - const Field3D& yup(std::vector::size_type index = 0) const { + const Field3D& yup(size_t index = 0) const { ASSERT2(index < yup_fields.size()); return yup_fields[index]; } /// Return reference to ydown field - Field3D& ydown(std::vector::size_type index = 0) { + Field3D& ydown(size_t index = 0) { ASSERT2(index < ydown_fields.size()); return ydown_fields[index]; } /// Return const reference to ydown field - const Field3D& ydown(std::vector::size_type index = 0) const { + const Field3D& ydown(size_t index = 0) const { ASSERT2(index < ydown_fields.size()); return ydown_fields[index]; } @@ -291,6 +321,17 @@ public: /// cuts on closed field lines? bool requiresTwistShift(bool twist_shift_enabled); + /// Enable a special tracking mode for debugging + /// Save all changes that, are done to the field, to tracking + Field3D& enableTracking(const std::string& name, std::weak_ptr tracking); + + /// Disable tracking + Field3D& disableTracking() { + tracking.reset(); + tracking_state = 0; + return *this; + } + ///////////////////////////////////////////////////////// // Data access @@ -312,11 +353,12 @@ public: const Region& getRegion(const std::string& region_name) const; /// Use region provided by the default, and if none is set, use the provided one const Region& getValidRegionWithDefault(const std::string& region_name) const; - void setRegion(const std::string& region_name); - void resetRegion() { regionID.reset(); }; - void setRegion(size_t id) { regionID = id; }; - void setRegion(std::optional id) { regionID = id; }; - std::optional getRegionID() const { return regionID; }; + void setRegion(const std::string& region_name) override; + void resetRegion() override { regionID.reset(); }; + void resetRegionParallel(bool force = false); + void setRegion(size_t id) override { regionID = id; }; + void setRegion(std::optional id) override { regionID = id; }; + std::optional getRegionID() const override { return regionID; }; /// Return a Region reference to use to iterate over the x- and /// y-indices of this field @@ -343,7 +385,7 @@ public: * Direct access to the underlying data array * * If CHECK > 2 then bounds checking is performed - * + * * If CHECK <= 2 then no checks are performed, to * allow inlining and optimisation of inner loops */ @@ -360,7 +402,7 @@ public: jz, nx, ny, nz); } #endif - return data[(jx * ny + jy) * nz + jz]; + return data[(((jx * ny) + jy) * nz) + jz]; } inline const BoutReal& operator()(int jx, int jy, int jz) const { @@ -375,7 +417,7 @@ public: jz, nx, ny, nz); } #endif - return data[(jx * ny + jy) * nz + jz]; + return data[(((jx * ny) + jy) * nz) + jz]; } /*! @@ -413,39 +455,110 @@ public: return &data[(jx * ny + jy) * nz]; } + struct View { + BoutReal* data; + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx) const { + return data[idx]; + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal& operator[](int idx) const { + return data[idx]; + } + + template + View& setScale(Mul /*unused*/, Div /*unused*/) { + static_assert(!std::is_same_v, + "Field3D::View does not support setScale()"); + return *this; + } + }; + operator View() { return View{&data[0]}; } + operator View() const { return View{const_cast(&data[0])}; } + //operator View() const { return View{&data[0]}; } + ///////////////////////////////////////////////////////// // Operators /// Assignment operators ///@{ Field3D& operator=(const Field3D& rhs); - Field3D& operator=(Field3D&& rhs); + Field3D& operator=(Field3D&& rhs) noexcept { + track(rhs, "operator="); + + // Move parallel slices or delete existing ones. + yup_fields = std::move(rhs.yup_fields); + ydown_fields = std::move(rhs.ydown_fields); + + // Move the data and data sizes + nx = rhs.nx; + ny = rhs.ny; + nz = rhs.nz; + regionID = rhs.regionID; + + data = std::move(rhs.data); + + // Move base slice last + Field::operator=(std::move(rhs)); + + return *this; + } Field3D& operator=(const Field2D& rhs); /// return void, as only part initialised void operator=(const FieldPerp& rhs); Field3D& operator=(BoutReal val); - ///@} + template + std::enable_if_t, Field3D&> + operator=(const BinaryExpr& expr) { + if (!isAllocated() || getMesh() != expr.getMesh()) { + *this = Field3D{expr}; + return *this; + } + + clearParallelSlices(); + setRegion(expr.getRegionID()); + setLocation(expr.getLocation()); + setDirections(expr.getDirections()); + allocate(); + expr.evaluate(&data[0]); + return *this; + } - /// Addition operators - ///@{ - Field3D& operator+=(const Field3D& rhs); - Field3D& operator+=(const Field2D& rhs); - Field3D& operator+=(BoutReal rhs); ///@} - /// Subtraction operators - ///@{ - Field3D& operator-=(const Field3D& rhs); - Field3D& operator-=(const Field2D& rhs); - Field3D& operator-=(BoutReal rhs); +#define FIELD3D_OP_EQUALS(OP_SYM) \ + template \ + std::enable_if_t< \ + is_expr_field3d_v || is_expr_field2d_v || is_expr_constant_v, Field3D&> \ + operator OP_SYM## = (const R& rhs) { \ + if (data.unique()) { \ + clearParallelSlices(); \ + auto expr = (*this)OP_SYM rhs; \ + expr.evaluate(&data[0]); \ + } else { \ + (*this) = (*this)OP_SYM rhs; \ + } \ + return *this; \ + } + + FIELD3D_OP_EQUALS(+) + FIELD3D_OP_EQUALS(-) + FIELD3D_OP_EQUALS(*) + FIELD3D_OP_EQUALS(/) + ///@} - /// Multiplication operators - ///@{ Field3D& operator*=(const Field3D& rhs); + Field3D& operator+=(const Field3D& rhs); + Field3D& operator-=(const Field3D& rhs); + Field3D& operator*=(const Field3DParallel& rhs); + Field3D& operator/=(const Field3DParallel& rhs); + Field3D& operator+=(const Field3DParallel& rhs); + Field3D& operator-=(const Field3DParallel& rhs); Field3D& operator*=(const Field2D& rhs); + Field3D& operator+=(const Field2D& rhs); + Field3D& operator-=(const Field2D& rhs); Field3D& operator*=(BoutReal rhs); - ///@} + Field3D& operator+=(BoutReal rhs); + Field3D& operator-=(BoutReal rhs); /// Division operators ///@{ @@ -454,8 +567,21 @@ public: Field3D& operator/=(BoutReal rhs); ///@} + Field3D& update_multiplication_inplace(const Field3D& rhs); + Field3D& update_division_inplace(const Field3D& rhs); + Field3D& update_addition_inplace(const Field3D& rhs); + Field3D& update_subtraction_inplace(const Field3D& rhs); + Field3D& update_multiplication_inplace(const Field2D& rhs); + Field3D& update_division_inplace(const Field2D& rhs); + Field3D& update_addition_inplace(const Field2D& rhs); + Field3D& update_subtraction_inplace(const Field2D& rhs); + Field3D& update_multiplication_inplace(BoutReal rhs); + Field3D& update_division_inplace(BoutReal rhs); + Field3D& update_addition_inplace(BoutReal rhs); + Field3D& update_subtraction_inplace(BoutReal rhs); + // FieldData virtual functions - bool is3D() const override { return true; } + FieldType field_type() const override { return FieldType::field3d; } #if CHECK > 0 void doneComms() override { bndry_xin = bndry_xout = bndry_yup = bndry_ydown = true; } @@ -466,7 +592,7 @@ public: friend class Vector3D; friend class Vector2D; - Field3D& calcParallelSlices(); + void calcParallelSlices() override; void applyBoundary(bool init = false) override; void applyBoundary(BoutReal t); @@ -479,8 +605,11 @@ public: /// This uses 2nd order central differences to set the value /// on the boundary to the value on the boundary in field \p f3d. /// Note: does not just copy values in boundary region. - void setBoundaryTo(const Field3D& f3d); + void setBoundaryTo(const Field3D& f3d) { setBoundaryTo(f3d, true); } + void setBoundaryTo(const Field3D& f3d, bool copyParallelSlices, + bool forceLegacy = false); + using FieldData::applyParallelBoundary; void applyParallelBoundary() override; void applyParallelBoundary(BoutReal t) override; void applyParallelBoundary(const std::string& condition) override; @@ -489,11 +618,20 @@ public: void applyParallelBoundary(const std::string& region, const std::string& condition, Field3D* f); + void swapData(Field3D& other); friend void swap(Field3D& first, Field3D& second) noexcept; int size() const override { return nx * ny * nz; }; -private: + std::weak_ptr getTracking() { return tracking; }; + + bool areCalcParallelSlicesAllowed() const { return _allowCalcParallelSlices; }; + void disallowCalcParallelSlices() { _allowCalcParallelSlices = false; }; + + inline Field3DParallel asField3DParallel(); + inline Field3DParallel asField3DParallel() const; + +protected: /// Array sizes (from fieldmesh). These are valid only if fieldmesh is not null int nx{-1}, ny{-1}, nz{-1}; @@ -508,41 +646,249 @@ private: /// RegionID over which the field is valid std::optional regionID; + + /// counter for tracking, to assign unique names to the variable names + int tracking_state{0}; + std::weak_ptr tracking; + + bool _allowCalcParallelSlices{true}; + // name is changed if we assign to the variable, while selfname is a + // non-changing copy that is used for the variable names in the dump files + std::string selfname; + template + void track(const T& change, const std::string& operation) { + if (tracking_state != 0) { + _track(change, operation); + } + } + template > + void _track(const T& change, std::string operation); + void _track(const BoutReal& change, std::string operation); + + template + static Array evaluateBinaryExpr(const BinaryExpr& expr) { + const auto* mesh = expr.getMesh(); + ASSERT1(mesh != nullptr); + + Array data{mesh->LocalNx * mesh->LocalNy * mesh->LocalNz}; + expr.evaluate(&data[0]); + return data; + } }; // Non-member overloaded operators +template +constexpr bool always_false = false; + // Binary operators FieldPerp operator+(const Field3D& lhs, const FieldPerp& rhs); FieldPerp operator-(const Field3D& lhs, const FieldPerp& rhs); FieldPerp operator*(const Field3D& lhs, const FieldPerp& rhs); FieldPerp operator/(const Field3D& lhs, const FieldPerp& rhs); -Field3D operator+(const Field3D& lhs, const Field3D& rhs); -Field3D operator-(const Field3D& lhs, const Field3D& rhs); -Field3D operator*(const Field3D& lhs, const Field3D& rhs); -Field3D operator/(const Field3D& lhs, const Field3D& rhs); +#define FIELD3D_FIELD3D_FIELD3D_OP(OP_SYM, OP_TYPE) \ + template && is_expr_field3d_v>> \ + BinaryExpr operator OP_SYM(const L& lhs, \ + const R& rhs) { \ + ASSERT1_EXPR_COMPATIBLE(lhs, rhs); \ + auto regionID = \ + lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID()); \ + return BinaryExpr{ \ + static_cast(lhs), \ + static_cast(rhs), \ + bout::op::OP_TYPE{}, \ + lhs.getMesh(), \ + lhs.getLocation(), \ + lhs.getDirections(), \ + regionID, \ + (regionID.has_value() ? lhs.getMesh()->getRegion(regionID.value()) \ + : lhs.getMesh()->getRegion("RGN_ALL"))}; \ + } + +FIELD3D_FIELD3D_FIELD3D_OP(+, Add) +FIELD3D_FIELD3D_FIELD3D_OP(-, Sub) +FIELD3D_FIELD3D_FIELD3D_OP(*, Mul) +FIELD3D_FIELD3D_FIELD3D_OP(/, Div) + +#define FIELD3D_FIELD3D_FIELD2D_OP(OP_SYM, OP_TYPE) \ + template \ + std::enable_if_t && is_expr_field2d_v, \ + BinaryExpr> \ + operator OP_SYM(const L& lhs, const R& rhs) { \ + ASSERT1_EXPR_COMPATIBLE(lhs, rhs); \ + auto regionID = lhs.getRegionID(); \ + int mesh_nz = lhs.getMesh()->LocalNz; \ + return BinaryExpr{ \ + static_cast(lhs), \ + static_cast(rhs).setScale(1, mesh_nz), \ + bout::op::OP_TYPE{}, \ + lhs.getMesh(), \ + lhs.getLocation(), \ + lhs.getDirections(), \ + regionID, \ + lhs.getMesh()->getRegion("RGN_ALL")}; \ + } + +FIELD3D_FIELD3D_FIELD2D_OP(+, Add) +FIELD3D_FIELD3D_FIELD2D_OP(-, Sub) +FIELD3D_FIELD3D_FIELD2D_OP(*, Mul) +FIELD3D_FIELD3D_FIELD2D_OP(/, Div) + +#define FIELD3D_FIELD3D_BOUTREAL_OP(OP_SYM, OP_TYPE) \ + template \ + std::enable_if_t && is_expr_constant_v, \ + BinaryExpr, bout::op::OP_TYPE>> \ + operator OP_SYM(const L& lhs, R rhs) { \ + auto regionID = lhs.getRegionID(); \ + return BinaryExpr, bout::op::OP_TYPE>{ \ + static_cast(lhs), \ + static_cast::View>(rhs), \ + bout::op::OP_TYPE{}, \ + lhs.getMesh(), \ + lhs.getLocation(), \ + lhs.getDirections(), \ + regionID, \ + lhs.getMesh()->getRegion("RGN_ALL")}; \ + } -Field3D operator+(const Field3D& lhs, const Field2D& rhs); -Field3D operator-(const Field3D& lhs, const Field2D& rhs); -Field3D operator*(const Field3D& lhs, const Field2D& rhs); -Field3D operator/(const Field3D& lhs, const Field2D& rhs); +FIELD3D_FIELD3D_BOUTREAL_OP(+, Add) +FIELD3D_FIELD3D_BOUTREAL_OP(-, Sub) +FIELD3D_FIELD3D_BOUTREAL_OP(*, Mul) +FIELD3D_FIELD3D_BOUTREAL_OP(/, Div) + +#define FIELD3D_BOUTREAL_FIELD3D_OP(OP_SYM, OP_TYPE) \ + template \ + std::enable_if_t && is_expr_field3d_v, \ + BinaryExpr, R, bout::op::OP_TYPE>> \ + operator OP_SYM(const L& lhs, const R& rhs) { \ + auto regionID = rhs.getRegionID(); \ + return BinaryExpr, R, bout::op::OP_TYPE>{ \ + static_cast::View>(lhs), \ + static_cast(rhs), \ + bout::op::OP_TYPE{}, \ + rhs.getMesh(), \ + rhs.getLocation(), \ + rhs.getDirections(), \ + regionID, \ + rhs.getMesh()->getRegion("RGN_ALL")}; \ + } -Field3D operator+(const Field3D& lhs, BoutReal rhs); -Field3D operator-(const Field3D& lhs, BoutReal rhs); -Field3D operator*(const Field3D& lhs, BoutReal rhs); -Field3D operator/(const Field3D& lhs, BoutReal rhs); +FIELD3D_BOUTREAL_FIELD3D_OP(+, Add) +FIELD3D_BOUTREAL_FIELD3D_OP(-, Sub) +FIELD3D_BOUTREAL_FIELD3D_OP(*, Mul) +FIELD3D_BOUTREAL_FIELD3D_OP(/, Div) + +template && is_expr_field3d_v>> +BinaryExpr if_else(bool condition, const L& lhs, + const R& rhs) { + ASSERT1_EXPR_COMPATIBLE(lhs, rhs); + auto regionID = lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID()); + return BinaryExpr{ + static_cast(lhs), + static_cast(rhs), + bout::op::IfElse{condition}, + lhs.getMesh(), + lhs.getLocation(), + lhs.getDirections(), + regionID, + (regionID.has_value() ? lhs.getMesh()->getRegion(regionID.value()) + : lhs.getMesh()->getRegion("RGN_ALL"))}; +} + +template +std::enable_if_t && is_expr_field2d_v, + BinaryExpr> +if_else(bool condition, const L& lhs, const R& rhs) { + ASSERT1_EXPR_COMPATIBLE(lhs, rhs); + auto regionID = lhs.getRegionID(); + int mesh_nz = lhs.getMesh()->LocalNz; + return BinaryExpr{ + static_cast(lhs), + static_cast(rhs).setScale(1, mesh_nz), + bout::op::IfElse{condition}, + lhs.getMesh(), + lhs.getLocation(), + lhs.getDirections(), + regionID, + lhs.getMesh()->getRegion("RGN_ALL")}; +} + +template +std::enable_if_t && is_expr_constant_v, + BinaryExpr, bout::op::IfElse>> +if_else(bool condition, const L& lhs, R rhs) { + auto regionID = lhs.getRegionID(); + return BinaryExpr, bout::op::IfElse>{ + static_cast(lhs), + static_cast::View>(rhs), + bout::op::IfElse{condition}, + lhs.getMesh(), + lhs.getLocation(), + lhs.getDirections(), + regionID, + lhs.getMesh()->getRegion("RGN_ALL")}; +} + +template +std::enable_if_t && is_expr_field3d_v, + BinaryExpr, R, bout::op::IfElse>> +if_else(bool condition, const L& lhs, const R& rhs) { + auto regionID = rhs.getRegionID(); + return BinaryExpr, R, bout::op::IfElse>{ + static_cast::View>(lhs), + static_cast(rhs), + bout::op::IfElse{condition}, + rhs.getMesh(), + rhs.getLocation(), + rhs.getDirections(), + regionID, + rhs.getMesh()->getRegion("RGN_ALL")}; +} -Field3D operator+(BoutReal lhs, const Field3D& rhs); -Field3D operator-(BoutReal lhs, const Field3D& rhs); -Field3D operator*(BoutReal lhs, const Field3D& rhs); -Field3D operator/(BoutReal lhs, const Field3D& rhs); +Field3DParallel operator+(const Field3D& lhs, const Field3DParallel& rhs); +Field3DParallel operator-(const Field3D& lhs, const Field3DParallel& rhs); +Field3DParallel operator*(const Field3D& lhs, const Field3DParallel& rhs); +Field3DParallel operator/(const Field3D& lhs, const Field3DParallel& rhs); + +Field3DParallel operator+(const Field3DParallel& lhs, const Field3D& rhs); +Field3DParallel operator-(const Field3DParallel& lhs, const Field3D& rhs); +Field3DParallel operator*(const Field3DParallel& lhs, const Field3D& rhs); +Field3DParallel operator/(const Field3DParallel& lhs, const Field3D& rhs); + +Field3DParallel operator+(const Field3DParallel& lhs, const Field3DParallel& rhs); +Field3DParallel operator-(const Field3DParallel& lhs, const Field3DParallel& rhs); +Field3DParallel operator*(const Field3DParallel& lhs, const Field3DParallel& rhs); +Field3DParallel operator/(const Field3DParallel& lhs, const Field3DParallel& rhs); + +Field3DParallel operator+(BoutReal lhs, const Field3DParallel& rhs); +Field3DParallel operator-(BoutReal lhs, const Field3DParallel& rhs); +Field3DParallel operator*(BoutReal lhs, const Field3DParallel& rhs); +Field3DParallel operator/(BoutReal lhs, const Field3DParallel& rhs); + +Field3DParallel operator+(const Field3DParallel& lhs, BoutReal rhs); +Field3DParallel operator-(const Field3DParallel& lhs, BoutReal rhs); +Field3DParallel operator*(const Field3DParallel& lhs, BoutReal rhs); +Field3DParallel operator/(const Field3DParallel& lhs, BoutReal rhs); /*! * Unary minus. Returns the negative of given field, * iterates over whole domain including guard/boundary cells. */ -Field3D operator-(const Field3D& f); +inline auto operator-(const Field3D& f) { + auto regionID = f.getRegionID(); + return BinaryExpr, Field3D, bout::op::Mul>{ + static_cast::View>(-1.0), + static_cast(f), + bout::op::Mul{}, + f.getMesh(), + f.getLocation(), + f.getDirections(), + regionID, + f.getRegion("RGN_ALL")}; +} // Non-member functions @@ -566,8 +912,8 @@ void checkData(const Field3D& f, const std::string& region = "RGN_NOBNDRY"); #else /// Ignored with disabled CHECK; Throw an exception if \p f is not /// allocated or if any elements are non-finite (for CHECK > 2) -inline void checkData(const Field3D& UNUSED(f), - const std::string& UNUSED(region) = "RGN_NOBNDRY"){}; +inline void checkData([[maybe_unused]] const Field3D& f, + [[maybe_unused]] const std::string& region = "RGN_NOBNDRY") {}; #endif /// Fourier filtering, removes all except one mode @@ -599,7 +945,7 @@ lowPass(const Field3D& var, int zmax, int keep_zonal, REGION rgn = RGN_ALL) { /// @param[in] var Variable to apply filter to /// @param[in] zmax Maximum mode in Z /// @param[in] rgn The region to calculate the result over -inline Field3D lowPass(const Field3D& var, int zmax, const std::string rgn = "RGN_ALL") { +inline Field3D lowPass(const Field3D& var, int zmax, const std::string& rgn = "RGN_ALL") { return lowPass(var, zmax, true, rgn); } @@ -628,7 +974,7 @@ Field2D DC(const Field3D& f, const std::string& rgn = "RGN_ALL"); #if CHECK > 2 void invalidateGuards(Field3D& var); #else -inline void invalidateGuards(Field3D& UNUSED(var)) {} +inline void invalidateGuards([[maybe_unused]] Field3D& var) {} #endif /// Returns a reference to the time-derivative of a field \p f @@ -639,7 +985,7 @@ inline Field3D& ddt(Field3D& f) { return *(f.timeDeriv()); } /// toString template specialisation /// Defined in utils.hxx template <> -inline std::string toString<>(const Field3D& UNUSED(val)) { +inline std::string toString<>([[maybe_unused]] const Field3D& val) { return ""; } @@ -650,4 +996,153 @@ bool operator==(const Field3D& a, const Field3D& b); /// Output a string describing a Field3D to a stream std::ostream& operator<<(std::ostream& out, const Field3D& value); +inline Field3D copy(const Field3D& f) { + Field3D result{f}; + result.allocate(); + for (size_t i = 0; i < result.numberParallelSlices(); ++i) { + result.yup(i).allocate(); + result.ydown(i).allocate(); + } + return result; +} + +/// Field3DParallel is intended to behave like Field3D, but preserve parallel +/// Fields. +/// Operations on Field3D, like multiplication, exp and floor only work on the +/// "main" field, Field3DParallel will retain the parallel slices. +class Field3DParallel : public Field3D { +public: + template + explicit Field3DParallel(Types... args) : Field3D(std::move(args)...) { + ensureFieldAligned(); + } + Field3DParallel(const Field3D& f) : Field3D(f) { ensureFieldAligned(); } + Field3DParallel(const Field3D& f, bool isRef) : Field3D(f), isRef(isRef) { + ensureFieldAligned(); + } + Field3DParallel(const Field2D& f) : Field3D(f) { ensureFieldAligned(); } + // Explicitly needed, as DirectionTypes is sometimes constructed from a + // brace enclosed list + explicit Field3DParallel(Mesh* localmesh = nullptr, CELL_LOC location_in = CELL_CENTRE, + DirectionTypes directions_in = {YDirectionType::Standard, + ZDirectionType::Standard}, + std::optional regionID = {}) + : Field3D(localmesh, location_in, directions_in, regionID) { + if (isFci()) { + splitParallelSlices(); + } + ensureFieldAligned(); + } + explicit Field3DParallel(Array data, Mesh* localmesh, + CELL_LOC location = CELL_CENTRE, + DirectionTypes directions_in = {YDirectionType::Standard, + ZDirectionType::Standard}) + : Field3D(std::move(data), localmesh, location, directions_in) { + ensureFieldAligned(); + } + explicit Field3DParallel(BoutReal, Mesh* mesh = nullptr); + Field3D& asField3D() { return *this; } + const Field3D& asField3D() const { return *this; } + + Field3DParallel& operator*=(const Field3D&); + Field3DParallel& operator/=(const Field3D&); + Field3DParallel& operator+=(const Field3D&); + Field3DParallel& operator-=(const Field3D&); + Field3DParallel& operator*=(const Field3DParallel&); + Field3DParallel& operator/=(const Field3DParallel&); + Field3DParallel& operator+=(const Field3DParallel&); + Field3DParallel& operator-=(const Field3DParallel&); + Field3DParallel& operator*=(BoutReal); + Field3DParallel& operator/=(BoutReal); + Field3DParallel& operator+=(BoutReal); + Field3DParallel& operator-=(BoutReal); + Field3DParallel& operator=(const Field3D& rhs) { + Field3D::operator=(rhs); + ensureFieldAligned(); + return *this; + } + Field3DParallel& operator=(Field3D&& rhs) { + Field3D::operator=(std::move(rhs)); + ensureFieldAligned(); + return *this; + } + Field3DParallel& operator=(BoutReal); + Field3DParallel& allocate(); + +private: + void ensureFieldAligned(); + bool isRef{false}; +}; + +Field3DParallel Field3D::asField3DParallel() { + if (isAllocated()) { + allocate(); + for (size_t i = 0; i < numberParallelSlices(); ++i) { + if (yup(i).isAllocated()) { + yup(i).allocate(); + } + if (ydown(i).isAllocated()) { + ydown(i).allocate(); + } + } + } + return Field3DParallel(*this, true); +} +Field3DParallel Field3D::asField3DParallel() const { return Field3DParallel(*this); } + +inline Field3D& Field3D::operator*=(const Field3DParallel& rhs) { + return (*this) *= rhs.asField3D(); +} + +inline Field3D& Field3D::operator/=(const Field3DParallel& rhs) { + return (*this) /= rhs.asField3D(); +} + +inline Field3D& Field3D::operator+=(const Field3DParallel& rhs) { + return (*this) += rhs.asField3D(); +} + +inline Field3D& Field3D::operator-=(const Field3DParallel& rhs) { + return (*this) -= rhs.asField3D(); +} + +template +struct is_expr_field3d> + : std::integral_constant>::value + || is_expr_field3d_v>> {}; + +Field3D operator+(const Field2D& lhs, const Field3DParallel& rhs); +Field3D operator-(const Field2D& lhs, const Field3DParallel& rhs); +Field3D operator*(const Field2D& lhs, const Field3DParallel& rhs); +Field3D operator/(const Field2D& lhs, const Field3DParallel& rhs); + +Field3D operator+(const Field3DParallel& lhs, const Field2D& rhs); +Field3D operator-(const Field3DParallel& lhs, const Field2D& rhs); +Field3D operator*(const Field3DParallel& lhs, const Field2D& rhs); +Field3D operator/(const Field3DParallel& lhs, const Field2D& rhs); + +inline Field3DParallel +filledFrom(const Field3DParallel& f, + const std::function& func) { + auto result{emptyFrom(f)}; + if (f.isFci()) { + BOUT_FOR(i, result.getRegion("RGN_NOY")) { result[i] = func(0, i); } + + for (size_t i = 0; i < result.numberParallelSlices(); ++i) { + result.yup(i).allocate(); + BOUT_FOR(d, result.yup(i).getValidRegionWithDefault("RGN_INVALID")) { + result.yup(i)[d] = func(i + 1, d); + } + result.ydown(i).allocate(); + BOUT_FOR(d, result.ydown(i).getValidRegionWithDefault("RGN_INVALID")) { + result.ydown(i)[d] = func(-i - 1, d); + } + } + } else { + BOUT_FOR(i, result.getRegion("RGN_ALL")) { result[i] = func(0, i); } + } + + return result; +} + #endif /* BOUT_FIELD3D_H */ diff --git a/include/bout/field_accessor.hxx b/include/bout/field_accessor.hxx index f942f38a11..35e1c7f417 100644 --- a/include/bout/field_accessor.hxx +++ b/include/bout/field_accessor.hxx @@ -58,10 +58,16 @@ struct FieldAccessor { /// Constructor from Field3D /// /// @param[in] f The field to access. Must already be allocated - explicit FieldAccessor(FieldType& f) : coords(f.getCoordinates()) { + explicit FieldAccessor(FieldType& f) { ASSERT0(f.getLocation() == location); ASSERT0(f.isAllocated()); + if (auto* Coords = f.getCoordinates()) { + coords = CoordinatesAccessor{Coords}; + } else { + coords = CoordinatesAccessor{}; + } + data = BoutRealArray{&f(0, 0, 0)}; // Field size @@ -82,15 +88,20 @@ struct FieldAccessor { ddt = BoutRealArray{&(f.timeDeriv()->operator()(0, 0, 0))}; } + explicit FieldAccessor(const FieldType& f) : FieldAccessor(const_cast(f)) {} + /// Provide shorthand for access to field data. /// Does not convert between 3D and 2D indices, /// so fa[i] is equivalent to fa.data[i]. /// BOUT_HOST_DEVICE inline const BoutReal& operator[](int ind) const { return data[ind]; } + BOUT_HOST_DEVICE inline BoutReal& operator[](int ind) { return data[ind]; } + BOUT_DEVICE inline BoutReal operator()(int i) const { return data[i]; } BOUT_HOST_DEVICE inline const BoutReal& operator[](const Ind3D& ind) const { return data[ind.ind]; } + BOUT_HOST_DEVICE inline BoutReal& operator[](const Ind3D& ind) { return data[ind.ind]; } // Pointers to the field data arrays // These are wrapped in BoutRealArray types so they can be indexed with Ind3D or int @@ -116,6 +127,9 @@ struct FieldAccessor { template using Field2DAccessor = FieldAccessor; +template +using Field3DAccessor = FieldAccessor; + /// Syntactic sugar for time derivative of a field /// /// Usage: @@ -131,4 +145,28 @@ BOUT_HOST_DEVICE inline BoutRealArray& ddt(const FieldAccessor(fa.ddt); } +struct FieldPerpAccessor { + FieldPerpAccessor() = delete; + + int nx, nz; + int yindex; + BoutReal* data; + + explicit FieldPerpAccessor(const FieldPerp& f) { + ASSERT0(f.isAllocated()); + + data = BoutRealArray{const_cast(&f(0, 0, 0))}; + + // Field size + nx = f.getNx(); + nz = f.getNz(); + + yindex = f.getIndex(); + } + + BOUT_HOST_DEVICE int getIndex() const { return yindex; } + BOUT_HOST_DEVICE inline const BoutReal& operator[](int ind) const { return data[ind]; } + BOUT_HOST_DEVICE inline BoutReal& operator[](int ind) { return data[ind]; } +}; + #endif diff --git a/include/bout/field_data.hxx b/include/bout/field_data.hxx index 185dcabf2d..20b6a517f9 100644 --- a/include/bout/field_data.hxx +++ b/include/bout/field_data.hxx @@ -6,7 +6,7 @@ * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -33,6 +33,7 @@ class FieldData; #include "bout/bout_types.hxx" #include "bout/unused.hxx" +#include #include #include #include @@ -40,12 +41,15 @@ class FieldData; class BoundaryOp; class BoundaryOpPar; +namespace bout::boundary { +class BoundaryRegionFCI; +} class Coordinates; class Mesh; #include "bout/boundary_region.hxx" class BoundaryRegionPar; -enum class BndryLoc; +enum class BndryLoc : std::int8_t; #include "bout/sys/expressionparser.hxx" @@ -71,13 +75,22 @@ public: /// Get variable location virtual CELL_LOC getLocation() const; + /// Enum to distinguish the different kinds of Fields + enum class FieldType : std::uint8_t { field3d, field2d, fieldperp }; + /// Is this an instance of `Field3D`, `Field2D`, or `FieldPerp`? + virtual FieldType field_type() const = 0; + // Defines interface which must be implemented /// True if variable is 3D - virtual bool is3D() const = 0; + [[deprecated("Use `field_type()` instead")]] + bool is3D() const { + return field_type() == FieldType::field3d; + } + /// Number of BoutReals in one element virtual int elementSize() const { return 1; } - virtual void doneComms(){}; // Notifies that communications done + virtual void doneComms() {}; // Notifies that communications done // Boundary conditions void setBoundary(const std::string& name); ///< Set the boundary conditions @@ -86,13 +99,13 @@ public: copyBoundary(const FieldData& f); ///< Copy the boundary conditions from another field virtual void applyBoundary(bool UNUSED(init) = false) {} - virtual void applyTDerivBoundary(){}; + virtual void applyTDerivBoundary() {}; - virtual void applyParallelBoundary(){}; - virtual void applyParallelBoundary(BoutReal UNUSED(t)){}; - virtual void applyParallelBoundary(const std::string& UNUSED(condition)){}; + virtual void applyParallelBoundary() {}; + virtual void applyParallelBoundary(BoutReal UNUSED(t)) {}; + virtual void applyParallelBoundary(const std::string& UNUSED(condition)) {}; virtual void applyParallelBoundary(const std::string& UNUSED(region), - const std::string& UNUSED(condition)){}; + const std::string& UNUSED(condition)) {}; // JMAD void addBndryFunction(FuncPtr userfunc, BndryLoc location); void addBndryGenerator(FieldGeneratorPtr gen, BndryLoc location); diff --git a/include/bout/fieldgroup.hxx b/include/bout/fieldgroup.hxx index 184766c6b8..440655e7ce 100644 --- a/include/bout/fieldgroup.hxx +++ b/include/bout/fieldgroup.hxx @@ -1,22 +1,23 @@ #ifndef BOUT_FIELDGROUP_H #define BOUT_FIELDGROUP_H -#include "bout/field_data.hxx" -#include - +#include #include #include #include -#include +class Field2D; +class Field3D; +class FieldPerp; +class Field; /// Group together fields for easier communication /// -/// Note: The FieldData class is used as a base class, -/// which is inherited by Field2D, Field3D, Vector2D and Vector3D -/// however Vector2D and Vector3D are stored by reference to their -/// components (x,y,z) as Field2D or Field3D objects. +/// Note: The `Field` class is used as a base class, +/// which is inherited by `Field2D`, `Field3D`, `FieldPerp`; +/// however `Vector2D` and `Vector3D` are stored by reference to their +/// components ``(x, y, z)`` as `Field2D` or `Field3D` objects. class FieldGroup { public: FieldGroup() = default; @@ -24,11 +25,9 @@ public: FieldGroup(FieldGroup&& other) = default; FieldGroup& operator=(const FieldGroup& other) = default; FieldGroup& operator=(FieldGroup&& other) = default; + ~FieldGroup() = default; - /// Constructor with a single FieldData \p f - FieldGroup(FieldData& f) { fvec.push_back(&f); } - - /// Constructor with a single Field3D \p f + FieldGroup(Field& f) { fvec.push_back(&f); } FieldGroup(Field3D& f) { fvec.push_back(&f); f3vec.push_back(&f); @@ -56,7 +55,7 @@ public: } /// Variadic constructor. Allows an arbitrary number of - /// FieldData arguments + /// Field arguments /// /// The explicit keyword prevents FieldGroup being constructed with arbitrary /// types. In particular arguments to add() cannot be implicitly converted @@ -78,12 +77,12 @@ public: return *this; } - /// Add a FieldData \p f to the group. + /// Add a Field \p f to the group. /// /// A pointer to this field will be stored internally, /// so the lifetime of this variable should be longer /// than the lifetime of this group. - void add(FieldData& f) { fvec.push_back(&f); } + void add(Field& f) { fvec.push_back(&f); } // Add a 3D field \p f, which goes into both vectors. // @@ -121,12 +120,8 @@ public: } /// Add multiple fields to this group - /// - /// This is a variadic template which allows Field3D objects to be - /// treated as a special case. An arbitrary number of fields can be - /// added. template - void add(FieldData& t, Ts&... ts) { + void add(Field& t, Ts&... ts) { add(t); // Add the first using functions above add(ts...); // Add the rest } @@ -165,16 +160,14 @@ public: } /// Iteration over all fields - using iterator = std::vector::iterator; - iterator begin() { return fvec.begin(); } - iterator end() { return fvec.end(); } + auto begin() { return fvec.begin(); } + auto end() { return fvec.end(); } /// Const iteration over all fields - using const_iterator = std::vector::const_iterator; - const_iterator begin() const { return fvec.begin(); } - const_iterator end() const { return fvec.end(); } + auto begin() const { return fvec.cbegin(); } + auto end() const { return fvec.cend(); } - const std::vector& get() const { return fvec; } + const std::vector& get() const { return fvec; } /// Iteration over 3D fields const std::vector& field3d() const { return f3vec; } @@ -183,8 +176,8 @@ public: void makeUnique(); private: - std::vector fvec; // Vector of fields - std::vector f3vec; // Vector of 3D fields + std::vector fvec; // Vector of fields + std::vector f3vec; // Vector of 3D fields }; /// Combine two FieldGroups diff --git a/include/bout/fieldops.hxx b/include/bout/fieldops.hxx new file mode 100644 index 0000000000..f36061ceaa --- /dev/null +++ b/include/bout/fieldops.hxx @@ -0,0 +1,454 @@ +#pragma once +#ifndef BOUT_FIELDOPS_HXX +#define BOUT_FIELDOPS_HXX + +#include "bout/array.hxx" +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/build_config.hxx" +#include "bout/build_defines.hxx" +#include "bout/region.hxx" + +#include +#include +#include +#include + +#if BOUT_HAS_CUDA +#include +#endif + +class Mesh; +class Field3D; +class Field3DParallel; +class Field2D; +class FieldPerp; + +namespace bout::detail { +// This is defined in field3d.cxx +// It is used because Mesh is an incomplete type so methods cannot be called +// in the template functions in this header file. +const Region& getField3DRegion(const Mesh* mesh, std::optional regionID); +} // namespace bout::detail + +template +struct is_expr_field2d : std::false_type {}; + +template <> +struct is_expr_field2d : std::true_type {}; + +template +inline constexpr bool is_expr_field2d_v = is_expr_field2d>::value; + +// Base template: nothing is an expression by default +template +struct is_expr_field3d : std::false_type {}; + +template <> +struct is_expr_field3d : std::true_type {}; + +template <> +struct is_expr_field3d : std::true_type {}; + +template +struct is_expr_fieldperp : std::false_type {}; + +template <> +struct is_expr_fieldperp : std::true_type {}; + +template +inline constexpr bool is_expr_fieldperp_v = is_expr_fieldperp>::value; + +// Helper variable template +template +inline constexpr bool is_expr_field3d_v = is_expr_field3d>::value; + +template +struct is_expr_constant : std::bool_constant> {}; + +template +inline constexpr bool is_expr_constant_v = is_expr_constant>::value; + +template +struct is_expr_constant> + : std::integral_constant>> {}; + +constexpr int THREADS = 128; +namespace bout { +namespace op { +struct Assign { + int scale = 1; + int offset = 0; + template + BOUT_HOST_DEVICE void operator()(int idx, BoutReal* out, const Expr& expr) const { + out[(idx * scale) + offset] = expr.lhs(idx) + expr.rhs(idx); + } +}; + +struct Add { + template + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx, const LView& L, + const RView& R) const { + return L(idx) + R(idx); + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(BoutReal a, BoutReal b) const { + return a + b; + } +}; +struct Sub { + template + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx, const LView& L, + const RView& R) const { + return L(idx) - R(idx); + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(BoutReal a, BoutReal b) const { + return a - b; + } +}; +struct Mul { + template + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx, const LView& L, + const RView& R) const { + return L(idx) * R(idx); + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(BoutReal a, BoutReal b) const { + return a * b; + } +}; +struct Div { + template + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx, const LView& L, + const RView& R) const { + return L(idx) / R(idx); + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(BoutReal a, BoutReal b) const { + return a / b; + } +}; +struct IfElse { + bool condition; + + template + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx, const LView& L, + const RView& R) const { + return condition ? L(idx) : R(idx); + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(BoutReal a, BoutReal b) const { + return condition ? a : b; + } +}; +}; // namespace op + +namespace reduce { + +struct Min { + struct State { + BoutReal value; + }; + + BOUT_HOST_DEVICE static State identity() { + return {std::numeric_limits::infinity()}; + } + BOUT_HOST_DEVICE static void accumulate(State& state, BoutReal value) { + state.value = value < state.value ? value : state.value; + } + BOUT_HOST_DEVICE static void combine(State& state, const State& other) { + state.value = other.value < state.value ? other.value : state.value; + } + static BoutReal finalize(const State& state) { return state.value; } +}; + +struct Max { + struct State { + BoutReal value; + }; + + BOUT_HOST_DEVICE static State identity() { + return {-std::numeric_limits::infinity()}; + } + BOUT_HOST_DEVICE static void accumulate(State& state, BoutReal value) { + state.value = value > state.value ? value : state.value; + } + BOUT_HOST_DEVICE static void combine(State& state, const State& other) { + state.value = other.value > state.value ? other.value : state.value; + } + static BoutReal finalize(const State& state) { return state.value; } +}; + +struct Mean { + struct State { + BoutReal sum; + int count; + }; + + BOUT_HOST_DEVICE static State identity() { return {0.0, 0}; } + BOUT_HOST_DEVICE static void accumulate(State& state, BoutReal value) { + state.sum += value; + state.count += 1; + } + BOUT_HOST_DEVICE static void combine(State& state, const State& other) { + state.sum += other.sum; + state.count += other.count; + } + static BoutReal finalize(const State& state) { + return state.sum / static_cast(state.count); + } +}; + +} // namespace reduce +}; // namespace bout + +template +struct ReductionView { + ExprView expr; + const int* indices; + int num_indices; + + BOUT_HOST_DEVICE BOUT_FORCEINLINE int size() const { return num_indices; } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal valueAtRegionPos(int idx) const { + return expr(indices[idx]); + } +}; + +template +ReductionView makeReductionView(const ExprView& expr, + const Array& indices) { + return ReductionView{expr, indices.size() > 0 ? &indices[0] : nullptr, + indices.size()}; +} + +#if BOUT_HAS_CUDA && defined(__CUDACC__) +template +__global__ void __launch_bounds__(THREADS) evaluatorExpr(BoutReal* out, const Expr expr) { + int tid = threadIdx.x + blockIdx.x * blockDim.x; + int e = expr.size(); + + // Out-of-bounds version + if (tid >= e) { + return; + } + int idx = expr.regionIdx(tid); + out[idx] = expr(idx); // single‐pass fusion + + // Grid-strided loop + //int stride = blockDim.x * gridDim.x; + //for (int i = tid; i < e; i += stride) { + // int idx = expr.regionIdx(i); + // out[idx] = expr(idx); // single‐pass fusion + //} +} + +template +__global__ void __launch_bounds__(THREADS) + reducerExpr(typename Reducer::State* partials, const ExprView expr) { + using State = typename Reducer::State; + + __shared__ State shared[THREADS]; + + const int tid = threadIdx.x; + const int global = blockIdx.x * blockDim.x + tid; + const int stride = blockDim.x * gridDim.x; + + State local = Reducer::identity(); + + for (int i = global; i < expr.size(); i += stride) { + Reducer::accumulate(local, expr.valueAtRegionPos(i)); + } + + shared[tid] = local; + __syncthreads(); + + for (int offset = blockDim.x / 2; offset > 0; offset /= 2) { + if (tid < offset) { + Reducer::combine(shared[tid], shared[tid + offset]); + } + __syncthreads(); + } + + if (tid == 0) { + partials[blockIdx.x] = shared[0]; + } +} +#endif + +#if BOUT_HAS_CUDA && defined(__CUDACC__) +struct StreamsRAII { + std::vector streams; + + cudaStream_t get() { + cudaStream_t stream = 0; + + if (streams.empty()) { + if (cudaStreamCreate(&stream) != cudaSuccess) { + throw BoutException("Failed to create CUDA stream"); + } + } else { + stream = streams.back(); + streams.pop_back(); + } + + return stream; + } + + void put(cudaStream_t stream) { streams.push_back(stream); } + + ~StreamsRAII() { + for (auto& stream : streams) { + cudaStreamDestroy(stream); + } + } + + StreamsRAII() = default; + StreamsRAII(const StreamsRAII&) = delete; + StreamsRAII(StreamsRAII&&) = delete; + StreamsRAII& operator=(const StreamsRAII&) = delete; + StreamsRAII& operator=(StreamsRAII&&) = delete; +}; +inline struct StreamsRAII streams; +#endif + +template +auto reduceExpr(const ExprView& expr_view) -> typename Reducer::State { + using State = typename Reducer::State; + + ASSERT1(expr_view.size() > 0); + +#if BOUT_HAS_CUDA && defined(__CUDACC__) + cudaStream_t stream = streams.get(); + int blocks = (expr_view.size() + THREADS - 1) / THREADS; + blocks = blocks < 1024 ? blocks : 1024; + Array partials(blocks); + + reducerExpr<<>>(&partials[0], expr_view); + cudaStreamSynchronize(stream); + streams.put(stream); + + State result = Reducer::identity(); + for (int i = 0; i < blocks; ++i) { + Reducer::combine(result, partials[i]); + } + return result; +#else + State result = Reducer::identity(); + for (int i = 0; i < expr_view.size(); ++i) { + Reducer::accumulate(result, expr_view.valueAtRegionPos(i)); + } + return result; +#endif +} + +template +struct BinaryExpr { + typename L::View lhs; + typename R::View rhs; + Array indices; + Func f; + + Mesh* mesh; + CELL_LOC location = CELL_CENTRE; + DirectionTypes directions; + std::optional regionID; + std::optional yindex; + + template + BinaryExpr(const typename L::View& lhs, const typename R::View& rhs, Func f, Mesh* mesh, + CELL_LOC location, DirectionTypes directions, std::optional regionID, + const Region& region, std::optional yindex = std::nullopt) + : lhs(lhs), rhs(rhs), indices(region.getLinearIndices()), f(f), mesh(mesh), + location(location), directions(directions), regionID(regionID), yindex(yindex) {} + + BinaryExpr(const typename L::View& lhs, const typename R::View& rhs, Func f, Mesh* mesh, + CELL_LOC location, DirectionTypes directions, std::optional regionID, + const Array& indices, std::optional yindex = std::nullopt) + : lhs(lhs), rhs(rhs), indices(indices), f(f), mesh(mesh), location(location), + directions(directions), regionID(regionID), yindex(yindex) {} + + BinaryExpr& operator=(const BinaryExpr&) = delete; + BinaryExpr& operator=(BinaryExpr&&) = delete; + + BOUT_HOST_DEVICE BOUT_FORCEINLINE int size() const { return indices.size(); } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx) const { + return f(idx, lhs, rhs); // single‐pass fusion + } + template + BOUT_HOST_DEVICE BOUT_FORCEINLINE auto operator[](const IndType& d) const + -> decltype(d.ind, BoutReal{}) { + if constexpr (std::is_same_v) { + return operator()(d.ind / d.nz); + } else { + return operator()(d.ind); + } + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE int regionIdx(int idx) const { return indices[idx]; } + + //operator ResT() { return ResT{*this}; } + struct View { + typename L::View lhs; + typename R::View rhs; + const int* indices; + int num_indices; + Func f; + int mul = 1; + int div = 1; + + View& setScale(int mul, int div) { + this->mul = mul; + this->div = div; + return *this; + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE int size() const { return num_indices; } + BOUT_HOST_DEVICE BOUT_FORCEINLINE int regionIdx(int idx) const { + return indices[idx]; + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx) const { + return f((idx * mul) / div, lhs, rhs); // single‐pass fusion + //return f(lhs((idx * mul) / div), rhs((idx * mul) / div)); // single‐pass fusion + } + }; + + operator View() { return View{lhs, rhs, &indices[0], indices.size(), f}; } + operator View() const { return View{lhs, rhs, &indices[0], indices.size(), f}; } + + void evaluate(BoutReal* data) const { +#if BOUT_HAS_CUDA && defined(__CUDACC__) + cudaStream_t stream = streams.get(); + int blocks = (size() + THREADS - 1) / THREADS; + evaluatorExpr<<>>(&data[0], static_cast(*this)); + cudaStreamSynchronize(stream); + streams.put(stream); +#else + if constexpr (std::is_same_v) { + // Optimize common case of Field3D on CPUs. + // Get the Region without directly using incomplete Mesh type + const auto& region = bout::detail::getField3DRegion(mesh, regionID); + for (auto block = region.getBlocks().cbegin(), end = region.getBlocks().cend(); + block < end; ++block) { + const int block_begin = block->first.ind; + const int block_end = block->second.ind; + + // Help optimizer establish loop bounds + BOUT_ASSUME(block_begin >= 0); + BOUT_ASSUME(block_begin <= block_end); + + for (int idx = block_begin; idx < block_end; ++idx) { + data[idx] = operator()(idx); + } + } + } else { + int e = size(); + for (int i = 0; i < e; ++i) { + int idx = regionIdx(i); + data[idx] = operator()(idx); // single‐pass fusion + } + } +#endif + } + + Mesh* getMesh() const { return mesh; } + CELL_LOC getLocation() const { return location; } + DirectionTypes getDirections() const { return directions; } + std::optional getRegionID() const { return regionID; }; + int getIndex() const { return yindex.value_or(-1); } +}; + +#endif // BOUT_FIELDSOPS_HXX diff --git a/include/bout/fieldperp.hxx b/include/bout/fieldperp.hxx index 6995308dbe..36a116e1b5 100644 --- a/include/bout/fieldperp.hxx +++ b/include/bout/fieldperp.hxx @@ -2,10 +2,10 @@ * Class for 2D X-Z slices * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * - * Contact: Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -28,24 +28,30 @@ class FieldPerp; #ifndef BOUT_FIELDPERP_H #define BOUT_FIELDPERP_H -#include "bout/field.hxx" - #include "bout/array.hxx" #include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/build_config.hxx" +#include "bout/field.hxx" +#include "bout/fieldops.hxx" #include "bout/region.hxx" - #include "bout/unused.hxx" +#include "bout/utils.hxx" +#include +#include #include #include +#include +#include class Field2D; // #include "bout/field2d.hxx" class Field3D; // #include "bout/field3d.hxx" /*! * Represents a 2D field perpendicular to the magnetic field - * at a particular index in Y, which only varies in X-Z. - * + * at a particular index in Y, which only varies in X-Z. + * * Primarily used inside field solvers */ class FieldPerp : public Field { @@ -58,7 +64,8 @@ public: FieldPerp(Mesh* fieldmesh = nullptr, CELL_LOC location_in = CELL_CENTRE, int yindex_in = -1, DirectionTypes directions_in = {YDirectionType::Standard, - ZDirectionType::Standard}); + ZDirectionType::Standard}, + std::optional regionID = {}); /*! * Copy constructor. After this the data @@ -86,6 +93,15 @@ public: DirectionTypes directions_in = {YDirectionType::Standard, ZDirectionType::Standard}); + template < + typename ResT, typename L, typename R, typename Func, + typename = std::enable_if_t<(is_expr_fieldperp_v && is_expr_fieldperp_v) + || (is_expr_constant_v && is_expr_fieldperp_v) + || (is_expr_fieldperp_v && is_expr_constant_v)>> + FieldPerp(const BinaryExpr& expr) + : FieldPerp(evaluateBinaryExpr(expr), expr.getMesh(), expr.getLocation(), + expr.getIndex(), expr.getDirections()) {} + ~FieldPerp() override = default; /*! @@ -94,6 +110,21 @@ public: FieldPerp& operator=(const FieldPerp& rhs); FieldPerp& operator=(FieldPerp&& rhs) = default; FieldPerp& operator=(BoutReal rhs); + template + std::enable_if_t || is_expr_constant_v, FieldPerp&> + operator=(const BinaryExpr& expr) { + if (!isAllocated() || getMesh() != expr.getMesh()) { + *this = FieldPerp{expr}; + return *this; + } + + setLocation(expr.getLocation()); + setDirections(expr.getDirections()); + setIndex(expr.getIndex()); + allocate(); + expr.evaluate(&data[0]); + return *this; + } /// Return a Region reference to use to iterate over this field const Region& getRegion(REGION region) const; @@ -157,6 +188,19 @@ public: return *this; } + FieldPerp& yup(std::vector::size_type UNUSED(index) = 0) { return *this; } + const FieldPerp& yup(std::vector::size_type UNUSED(index) = 0) const { + return *this; + } + + FieldPerp& ydown(std::vector::size_type UNUSED(index) = 0) { return *this; } + const FieldPerp& ydown(std::vector::size_type UNUSED(index) = 0) const { + return *this; + } + + FieldPerp& ynext(int UNUSED(dir)) { return *this; } + const FieldPerp& ynext(int UNUSED(dir)) const { return *this; } + /*! * Ensure that data array is allocated and unique */ @@ -191,7 +235,7 @@ public: /*! * Access to the underlying data array at a given x,z index - * + * * If CHECK > 2 then bounds checking is performed, otherwise * no checks are performed */ @@ -206,7 +250,7 @@ public: jx, jz, nx, nz); } #endif - return data[jx * nz + jz]; + return data[(jx * nz) + jz]; } /*! @@ -223,13 +267,13 @@ public: jx, jz, nx, nz); } #endif - return data[jx * nz + jz]; + return data[(jx * nz) + jz]; } /*! - * Access to the underlying data array. (X,Y,Z) indices for consistency with + * Access to the underlying data array. (X,Y,Z) indices for consistency with * other field types - * + * */ BoutReal& operator()(int jx, int UNUSED(jy), int jz) { return (*this)(jx, jz); } @@ -238,7 +282,7 @@ public: } /*! - * Addition, modifying in-place. + * Addition, modifying in-place. * This loops over the entire domain, including guard/boundary cells */ FieldPerp& operator+=(const FieldPerp& rhs); @@ -247,7 +291,7 @@ public: FieldPerp& operator+=(BoutReal rhs); /*! - * Subtraction, modifying in place. + * Subtraction, modifying in place. * This loops over the entire domain, including guard/boundary cells */ FieldPerp& operator-=(const FieldPerp& rhs); @@ -256,7 +300,7 @@ public: FieldPerp& operator-=(BoutReal rhs); /*! - * Multiplication, modifying in place. + * Multiplication, modifying in place. * This loops over the entire domain, including guard/boundary cells */ FieldPerp& operator*=(const FieldPerp& rhs); @@ -265,7 +309,7 @@ public: FieldPerp& operator*=(BoutReal rhs); /*! - * Division, modifying in place. + * Division, modifying in place. * This loops over the entire domain, including guard/boundary cells */ FieldPerp& operator/=(const FieldPerp& rhs); @@ -286,13 +330,43 @@ public: */ int getNz() const override { return nz; }; - bool is3D() const override { return false; } + FieldType field_type() const override { return FieldType::fieldperp; } friend void swap(FieldPerp& first, FieldPerp& second) noexcept; int size() const override { return nx * nz; }; + struct View { + BoutReal* data; + int mul = 1; + int div = 1; + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx) const { + return data[(idx * mul) / div]; + } + BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal& operator[](int idx) const { + return data[(idx * mul) / div]; + } + + View& setScale(int mul, int div) { + this->mul = mul; + this->div = div; + return *this; + } + }; + operator View() { return View{&data[0]}; } + operator View() const { return View{const_cast(&data[0])}; } + private: + template + static Array evaluateBinaryExpr(const BinaryExpr& expr) { + const auto* mesh = expr.getMesh(); + ASSERT1(mesh != nullptr); + + Array data{mesh->LocalNx * mesh->LocalNz}; + expr.evaluate(&data[0]); + return data; + } + /// The Y index at which this FieldPerp is defined int yindex{-1}; @@ -335,7 +409,18 @@ FieldPerp operator/(BoutReal lhs, const FieldPerp& rhs); * Unary minus. Returns the negative of given field, * iterates over whole domain including guard/boundary cells. */ -FieldPerp operator-(const FieldPerp& f); +inline auto operator-(const FieldPerp& f) { + return BinaryExpr, FieldPerp, bout::op::Mul>{ + static_cast::View>(-1.0), + static_cast(f), + bout::op::Mul{}, + f.getMesh(), + f.getLocation(), + f.getDirections(), + std::nullopt, + f.getRegion("RGN_ALL"), + f.getIndex()}; +} /// Create a FieldPerp by slicing a 3D field at a given y const FieldPerp sliceXZ(const Field3D& f, int y); @@ -379,4 +464,13 @@ bool operator==(const FieldPerp& a, const FieldPerp& b); /// Output a string describing a FieldPerp to a stream std::ostream& operator<<(std::ostream& out, const FieldPerp& value); +template +struct is_expr_fieldperp> + : std::integral_constant> + && is_expr_fieldperp_v>) + || (is_expr_constant_v> + && is_expr_fieldperp_v>) + || (is_expr_fieldperp_v> + && is_expr_constant_v>)> {}; + #endif diff --git a/include/bout/fv_ops.hxx b/include/bout/fv_ops.hxx index 52edde4bda..b477ff7c34 100644 --- a/include/bout/fv_ops.hxx +++ b/include/bout/fv_ops.hxx @@ -5,11 +5,17 @@ #ifndef BOUT_FV_OPS_H #define BOUT_FV_OPS_H +#include "boutexception.hxx" +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/build_defines.hxx" +#include "bout/coordinates.hxx" +#include "bout/field.hxx" #include "bout/field3d.hxx" #include "bout/globals.hxx" -#include "bout/vector2d.hxx" - +#include "bout/region.hxx" #include "bout/utils.hxx" +#include "bout/vector2d.hxx" #include namespace FV { @@ -106,7 +112,7 @@ struct Fromm { /*! * Second order slope limiter method - * + * * Limits slope to minimum absolute value * of left and right gradients. If at a maximum * or minimum slope set to zero, i.e. reverts @@ -116,7 +122,7 @@ struct MinMod { void operator()(Stencil1D& n) { // Choose the gradient within the cell // as the minimum (smoothest) solution - BoutReal slope = _minmod(n.p - n.c, n.c - n.m); + const BoutReal slope = _minmod(n.p - n.c, n.c - n.m); n.L = n.c - 0.5 * slope; n.R = n.c + 0.5 * slope; } @@ -143,17 +149,17 @@ private: /*! * Monotonised Central (MC) second order slope limiter (Van Leer) - * - * Limits the slope based on taking the slope with + * + * Limits the slope based on taking the slope with * the minimum absolute value from central, 2*left and * 2*right. If any of these slopes have different signs * then the slope reverts to zero (i.e. 1st-order upwinding). */ struct MC { void operator()(Stencil1D& n) { - BoutReal slope = minmod(2. * (n.p - n.c), // 2*right difference - 0.5 * (n.p - n.m), // Central difference - 2. * (n.c - n.m)); // 2*left difference + const BoutReal slope = minmod(2. * (n.p - n.c), // 2*right difference + 0.5 * (n.p - n.m), // Central difference + 2. * (n.c - n.m)); // 2*left difference n.L = n.c - 0.5 * slope; n.R = n.c + 0.5 * slope; } @@ -172,6 +178,97 @@ private: } }; +/*! + * Symmetric Van Albada second order slope limiter + * + * Uses a smooth (differentiable) approximation to `max(a*b, 0)` to avoid + * introducing a kink at extrema, which can be helpful for nonlinear solvers + * and finite-difference Jacobian calculations. + * + * The limited slope is calculated from the left and right differences + * `dl = c - m` and `dr = p - c` as + * + * slope = (pos(dl*dr) * (dl + dr)) / (dl^2 + dr^2) + * + * where `pos(x)` is a smooth approximation to `max(x, 0)`. + */ +struct VanAlbada { + void operator()(Stencil1D& n) { + const BoutReal dl = n.c - n.m; + const BoutReal dr = n.p - n.c; + + const BoutReal denom = dl * dl + dr * dr; + + // Smoothness parameters: + // - keep division well-defined when dl=dr=0 + // - provide a differentiable approximation to max(dl*dr, 0) + const BoutReal eps = 1e-12 * denom + 1e-30; + + const BoutReal ab = dl * dr; + const BoutReal ab_pos = 0.5 * (ab + sqrt(ab * ab + eps * eps)); + + const BoutReal slope = (ab_pos * (dl + dr)) / (denom + eps); + + n.L = n.c - 0.5 * slope; + n.R = n.c + 0.5 * slope; + } +}; + +/*! + * WENO3-JS (Jiang-Shu) reconstruction to cell faces + * + * This is a third-order essentially non-oscillatory reconstruction using two + * candidate second-order polynomials and smoothness-weighted blending. + * + * Unlike TVD slope limiters (e.g. ``MC``), WENO reconstruction is generally + * smooth (differentiable) for all inputs, but it does not enforce strict + * monotonicity. + * + * Uses only the three-point stencil (`m`, `c`, `p`), so it is a drop-in + * replacement anywhere `Stencil1D` is populated with those values. + */ +struct WENO3 { + void operator()(Stencil1D& n) { + // Right face (between c and p): value from cell c (left state at i+1/2) + const BoutReal p0_r = 0.5 * (-n.m + 3.0 * n.c); + const BoutReal p1_r = 0.5 * (n.c + n.p); + + const BoutReal beta0_r = SQ(n.c - n.m); + const BoutReal beta1_r = SQ(n.p - n.c); + + // Left face (between m and c): value from cell c (right state at i-1/2) + const BoutReal p0_l = 0.5 * (-n.p + 3.0 * n.c); + const BoutReal p1_l = 0.5 * (n.m + n.c); + + const BoutReal beta0_l = beta1_r; + const BoutReal beta1_l = beta0_r; + + // Smoothness parameter (scaled to local variation) + const BoutReal eps = 1e-12 * (beta0_r + beta1_r) + 1e-30; + + // Linear weights for WENO3-JS + constexpr BoutReal d0 = 1.0 / 3.0; + constexpr BoutReal d1 = 2.0 / 3.0; + + // Right face weights + const BoutReal a0_r = d0 / SQ(eps + beta0_r); + const BoutReal a1_r = d1 / SQ(eps + beta1_r); + const BoutReal wsum_r = a0_r + a1_r; + const BoutReal w0_r = a0_r / wsum_r; + const BoutReal w1_r = a1_r / wsum_r; + + // Left face weights (mirrored) + const BoutReal a0_l = d0 / SQ(eps + beta0_l); + const BoutReal a1_l = d1 / SQ(eps + beta1_l); + const BoutReal wsum_l = a0_l + a1_l; + const BoutReal w0_l = a0_l / wsum_l; + const BoutReal w1_l = a1_l / wsum_l; + + n.R = w0_r * p0_r + w1_r * p1_r; + n.L = w0_l * p0_l + w1_l * p1_l; + } +}; + /*! * Communicate fluxes between processors * Takes values in guard cells, and adds them to cells @@ -195,8 +292,8 @@ void communicateFluxes(Field3D& f); /// /// NB: Uses to/from FieldAligned coordinates template -const Field3D Div_par(const Field3D& f_in, const Field3D& v_in, - const Field3D& wave_speed_in, bool fixflux = true) { +Field3D Div_par(const Field3D& f_in, const Field3D& v_in, const Field3D& wave_speed_in, + bool fixflux = true) { ASSERT1_FIELDS_COMPATIBLE(f_in, v_in); ASSERT1_FIELDS_COMPATIBLE(f_in, wave_speed_in); @@ -252,19 +349,19 @@ const Field3D Div_par(const Field3D& f_in, const Field3D& v_in, BoutReal common_factor = (coord->J(i, j) + coord->J(i, j + 1)) / (sqrt(coord->g_22(i, j)) + sqrt(coord->g_22(i, j + 1))); - BoutReal flux_factor_rc = common_factor / (coord->dy(i, j) * coord->J(i, j)); - BoutReal flux_factor_rp = + const BoutReal flux_factor_rc = common_factor / (coord->dy(i, j) * coord->J(i, j)); + const BoutReal flux_factor_rp = common_factor / (coord->dy(i, j + 1) * coord->J(i, j + 1)); // For left cell boundaries common_factor = (coord->J(i, j) + coord->J(i, j - 1)) / (sqrt(coord->g_22(i, j)) + sqrt(coord->g_22(i, j - 1))); - BoutReal flux_factor_lc = common_factor / (coord->dy(i, j) * coord->J(i, j)); - BoutReal flux_factor_lm = + const BoutReal flux_factor_lc = common_factor / (coord->dy(i, j) * coord->J(i, j)); + const BoutReal flux_factor_lm = common_factor / (coord->dy(i, j - 1) * coord->J(i, j - 1)); #endif - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { #if BOUT_USE_METRIC_3D // For right cell boundaries BoutReal common_factor = @@ -309,7 +406,7 @@ const Field3D Div_par(const Field3D& f_in, const Field3D& v_in, if (mesh->lastY(i) && (j == mesh->yend) && !mesh->periodicY(i)) { // Last point in domain - BoutReal bndryval = 0.5 * (s.c + s.p); + const BoutReal bndryval = 0.5 * (s.c + s.p); if (fixflux) { // Use mid-point to be consistent with boundary conditions flux = bndryval * vpar; @@ -320,7 +417,7 @@ const Field3D Div_par(const Field3D& f_in, const Field3D& v_in, } else { // Maximum wave speed in the two cells - BoutReal amax = BOUTMAX(wave_speed(i, j, k), wave_speed(i, j + 1, k)); + const BoutReal amax = BOUTMAX(wave_speed(i, j, k), wave_speed(i, j + 1, k)); if (vpar > amax) { // Supersonic flow out of this cell @@ -344,7 +441,7 @@ const Field3D Div_par(const Field3D& f_in, const Field3D& v_in, if (mesh->firstY(i) && (j == mesh->ystart) && !mesh->periodicY(i)) { // First point in domain - BoutReal bndryval = 0.5 * (s.c + s.m); + const BoutReal bndryval = 0.5 * (s.c + s.m); if (fixflux) { // Use mid-point to be consistent with boundary conditions flux = bndryval * vpar; @@ -355,7 +452,7 @@ const Field3D Div_par(const Field3D& f_in, const Field3D& v_in, } else { // Maximum wave speed in the two cells - BoutReal amax = BOUTMAX(wave_speed(i, j, k), wave_speed(i, j - 1, k)); + const BoutReal amax = BOUTMAX(wave_speed(i, j, k), wave_speed(i, j - 1, k)); if (vpar < -amax) { // Supersonic out of this cell @@ -380,16 +477,16 @@ const Field3D Div_par(const Field3D& f_in, const Field3D& v_in, * Div ( n * v ) -- Magnetic drifts * * This uses the expression - * + * * Div( A ) = 1/J * d/di ( J * A^i ) - * + * * Hence the input vector should be contravariant * * Note: Uses to/from FieldAligned * */ template -const Field3D Div_f_v(const Field3D& n_in, const Vector3D& v, bool bndry_flux) { +Field3D Div_f_v(const Field3D& n_in, const Vector3D& v, bool bndry_flux) { ASSERT1(n_in.getLocation() == v.getLocation()); ASSERT1_FIELDS_COMPATIBLE(n_in, v.x); @@ -412,10 +509,10 @@ const Field3D Div_f_v(const Field3D& n_in, const Vector3D& v, bool bndry_flux) { BOUT_FOR(i, result.getRegion("RGN_NOBNDRY")) { // Calculate velocities - BoutReal vU = 0.25 * (vz[i.zp()] + vz[i]) * (coord->J[i.zp()] + coord->J[i]); - BoutReal vD = 0.25 * (vz[i.zm()] + vz[i]) * (coord->J[i.zm()] + coord->J[i]); - BoutReal vL = 0.25 * (vx[i.xm()] + vx[i]) * (coord->J[i.xm()] + coord->J[i]); - BoutReal vR = 0.25 * (vx[i.xp()] + vx[i]) * (coord->J[i.xp()] + coord->J[i]); + const BoutReal vU = 0.25 * (vz[i.zp()] + vz[i]) * (coord->J[i.zp()] + coord->J[i]); + const BoutReal vD = 0.25 * (vz[i.zm()] + vz[i]) * (coord->J[i.zm()] + coord->J[i]); + const BoutReal vL = 0.25 * (vx[i.xm()] + vx[i]) * (coord->J[i.xm()] + coord->J[i]); + const BoutReal vR = 0.25 * (vx[i.xp()] + vx[i]) * (coord->J[i.xp()] + coord->J[i]); // X direction Stencil1D s; @@ -445,7 +542,7 @@ const Field3D Div_f_v(const Field3D& n_in, const Vector3D& v, bool bndry_flux) { // Not at a boundary if (vR > 0.0) { // Flux out into next cell - BoutReal flux = vR * s.R; + const BoutReal flux = vR * s.R; result[i] += flux / (coord->dx[i] * coord->J[i]); result[i.xp()] -= flux / (coord->dx[i.xp()] * coord->J[i.xp()]); } @@ -471,7 +568,7 @@ const Field3D Div_f_v(const Field3D& n_in, const Vector3D& v, bool bndry_flux) { } else { // Not at a boundary if (vL < 0.0) { - BoutReal flux = vL * s.L; + const BoutReal flux = vL * s.L; result[i] -= flux / (coord->dx[i] * coord->J[i]); result[i.xm()] += flux / (coord->dx[i.xm()] * coord->J[i.xm()]); } @@ -488,12 +585,12 @@ const Field3D Div_f_v(const Field3D& n_in, const Vector3D& v, bool bndry_flux) { cellboundary(s); if (vU > 0.0) { - BoutReal flux = vU * s.R; + const BoutReal flux = vU * s.R; result[i] += flux / (coord->J[i] * coord->dz[i]); result[i.zp()] -= flux / (coord->J[i.zp()] * coord->dz[i.zp()]); } if (vD < 0.0) { - BoutReal flux = vD * s.L; + const BoutReal flux = vD * s.L; result[i] -= flux / (coord->J[i] * coord->dz[i]); result[i.zm()] += flux / (coord->J[i.zm()] * coord->dz[i.zm()]); } @@ -513,13 +610,13 @@ const Field3D Div_f_v(const Field3D& n_in, const Vector3D& v, bool bndry_flux) { BOUT_FOR(i, result.getRegion("RGN_NOBNDRY")) { // Y velocities on y boundaries - BoutReal vU = 0.25 * (vy[i] + vy[i.yp()]) * (coord->J[i] + coord->J[i.yp()]); - BoutReal vD = 0.25 * (vy[i] + vy[i.ym()]) * (coord->J[i] + coord->J[i.ym()]); + const BoutReal vU = 0.25 * (vy[i] + vy[i.yp()]) * (coord->J[i] + coord->J[i.yp()]); + const BoutReal vD = 0.25 * (vy[i] + vy[i.ym()]) * (coord->J[i] + coord->J[i.ym()]); // n (advected quantity) on y boundaries // Note: Use unshifted n_in variable - BoutReal nU = 0.5 * (n[i] + n[i.yp()]); - BoutReal nD = 0.5 * (n[i] + n[i.ym()]); + const BoutReal nU = 0.5 * (n[i] + n[i.yp()]); + const BoutReal nD = 0.5 * (n[i] + n[i.ym()]); yresult[i] = (nU * vU - nD * vD) / (coord->J[i] * coord->dy[i]); } diff --git a/include/bout/globalindexer.hxx b/include/bout/globalindexer.hxx index e756ead3b2..bd4203a092 100644 --- a/include/bout/globalindexer.hxx +++ b/include/bout/globalindexer.hxx @@ -86,7 +86,7 @@ public: int localSize = size(); MPI_Comm comm = - std::is_same_v ? fieldmesh->getXcomm() : BoutComm::get(); + std::is_same_v ? fieldmesh->getXZcomm() : BoutComm::get(); fieldmesh->getMpi().MPI_Scan(&localSize, &globalEnd, 1, MPI_INT, MPI_SUM, comm); globalEnd--; int counter = globalStart = globalEnd - size() + 1; diff --git a/include/bout/gyro_average.hxx b/include/bout/gyro_average.hxx index 665024797d..9ce68871cc 100644 --- a/include/bout/gyro_average.hxx +++ b/include/bout/gyro_average.hxx @@ -1,17 +1,17 @@ /*!************************************************************ * \file gyro_average.hxx - * + * * Gyro-averaging operators * * * 2010-09-03 Ben Dudson * * Initial version, simple averaging operator - * + * ************************************************************** * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify diff --git a/include/bout/hypre_interface.hxx b/include/bout/hypre_interface.hxx index ae392de4f3..189e91d159 100644 --- a/include/bout/hypre_interface.hxx +++ b/include/bout/hypre_interface.hxx @@ -159,7 +159,7 @@ public: explicit HypreVector(IndexerPtr indConverter) : indexConverter(indConverter) { Mesh& mesh = *indConverter->getMesh(); const MPI_Comm comm = - std::is_same_v ? mesh.getXcomm() : BoutComm::get(); + std::is_same_v ? mesh.getXZcomm() : BoutComm::get(); HYPRE_BigInt jlower = indConverter->getGlobalStart(); HYPRE_BigInt jupper = jlower + indConverter->size() - 1; // inclusive end @@ -380,7 +380,7 @@ public: : hypre_matrix(new HYPRE_IJMatrix, MatrixDeleter{}), index_converter(indConverter) { Mesh* mesh = indConverter->getMesh(); const MPI_Comm comm = - std::is_same_v ? mesh->getXcomm() : BoutComm::get(); + std::is_same_v ? mesh->getXZcomm() : BoutComm::get(); parallel_transform = &mesh->getCoordinates()->getParallelTransform(); ilower = indConverter->getGlobalStart(); @@ -444,19 +444,19 @@ public: value = matrix->getVal(row, column); } Element& operator=(const Element& other) { - AUTO_TRACE(); + ASSERT3(finite(static_cast(other))); return *this = static_cast(other); } Element& operator=(BoutReal value_) { - AUTO_TRACE(); + ASSERT3(finite(value_)); value = value_; setValues(value); return *this; } Element& operator+=(BoutReal value_) { - AUTO_TRACE(); + ASSERT3(finite(value_)); auto column_position = std::find(cbegin(positions), cend(positions), column); if (column_position != cend(positions)) { @@ -812,7 +812,7 @@ public: "values are: gmres, bicgstab, pcg") .withDefault(HYPRE_SOLVER_TYPE::bicgstab); - comm = std::is_same_v ? mesh.getXcomm() : BoutComm::get(); + comm = std::is_same_v ? mesh.getXZcomm() : BoutComm::get(); auto print_level = options["hypre_print_level"] diff --git a/include/bout/index_derivs.hxx b/include/bout/index_derivs.hxx index 456f98f8c2..a8d1bbb1d3 100644 --- a/include/bout/index_derivs.hxx +++ b/include/bout/index_derivs.hxx @@ -43,7 +43,6 @@ #include #include #include -#include #include #include @@ -84,7 +83,7 @@ class DerivativeType { public: template void standard(const T& var, T& result, const std::string& region) const { - AUTO_TRACE(); + ASSERT2(meta.derivType == DERIV::Standard || meta.derivType == DERIV::StandardSecond || meta.derivType == DERIV::StandardFourth) ASSERT2(var.getMesh()->getNguard(direction) >= nGuards); @@ -98,7 +97,7 @@ public: template void upwindOrFlux(const T& vel, const T& var, T& result, const std::string& region) const { - AUTO_TRACE(); + ASSERT2(meta.derivType == DERIV::Upwind || meta.derivType == DERIV::Flux) ASSERT2(var.getMesh()->getNguard(direction) >= nGuards); @@ -133,7 +132,7 @@ struct registerMethod { template void operator()(Direction, Stagger, FieldTypeContainer, Method) { - AUTO_TRACE(); + using namespace std::placeholders; // Now we want to get the actual field type out of the TypeContainer @@ -149,7 +148,7 @@ struct registerMethod { // removed and we can use nGuard directly in the template statement. const int nGuards = method.meta.nGuards; - auto& derivativeRegister = DerivativeStore::getInstance(); + auto& derivativeRegister = getStore(); switch (method.meta.derivType) { case (DERIV::Standard): diff --git a/include/bout/index_derivs_interface.hxx b/include/bout/index_derivs_interface.hxx index 8f7e41a68e..cbb1e5ac53 100644 --- a/include/bout/index_derivs_interface.hxx +++ b/include/bout/index_derivs_interface.hxx @@ -29,6 +29,8 @@ #ifndef __INDEX_DERIVS_INTERFACE_HXX__ #define __INDEX_DERIVS_INTERFACE_HXX__ +#include "bout/boutexception.hxx" +#include "bout/field3d.hxx" #include "bout/traits.hxx" #include #include @@ -45,7 +47,6 @@ namespace index { template T flowDerivative(const T& vel, const T& f, CELL_LOC outloc, const std::string& method, const std::string& region) { - AUTO_TRACE(); // Checks static_assert(bout::utils::is_Field2D_v || bout::utils::is_Field3D_v, @@ -110,7 +111,6 @@ T flowDerivative(const T& vel, const T& f, CELL_LOC outloc, const std::string& m template T standardDerivative(const T& f, CELL_LOC outloc, const std::string& method, const std::string& region) { - AUTO_TRACE(); // Checks static_assert(bout::utils::is_Field2D_v || bout::utils::is_Field3D_v, @@ -150,8 +150,8 @@ T standardDerivative(const T& f, CELL_LOC outloc, const std::string& method, } // Lookup the method - auto derivativeMethod = DerivativeStore::getInstance().getStandardDerivative( - method, direction, stagger, derivType); + auto derivativeMethod = + getStore().getStandardDerivative(method, direction, stagger, derivType); // Create the result field T result{emptyFrom(f).setLocation(outloc)}; @@ -174,14 +174,20 @@ T standardDerivative(const T& f, CELL_LOC outloc, const std::string& method, template T DDX(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } +inline Field3D DDX(const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY") { + return DDX(f.asField3D(), outloc, method, region); +} + template T D2DX2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } @@ -189,7 +195,7 @@ T D2DX2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T D4DX4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } @@ -199,24 +205,33 @@ T D4DX4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T DDY(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); - if (f.hasParallelSlices()) { + + if (f.isFci() or f.hasParallelSlices()) { ASSERT1(f.getDirectionY() == YDirectionType::Standard); + if (!f.hasParallelSlices()) { + throw BoutException( + "parallel slices needed for parallel derivatives. Make sure to communicate and " + "apply parallel boundary conditions before calling derivative"); + } return standardDerivative(f, outloc, method, region); - } else { - const bool is_unaligned = (f.getDirectionY() == YDirectionType::Standard); - const T f_aligned = is_unaligned ? toFieldAligned(f, "RGN_NOX") : f; - T result = standardDerivative(f_aligned, outloc, - method, region); - return is_unaligned ? fromFieldAligned(result, region) : result; } + const bool is_unaligned = (f.getDirectionY() == YDirectionType::Standard); + const T f_aligned = is_unaligned ? toFieldAligned(f, "RGN_NOX") : f; + T result = standardDerivative(f_aligned, outloc, + method, region); + return is_unaligned ? fromFieldAligned(result, region) : result; +} +inline Field3D DDY(const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY") { + return DDY(f.asField3D(), outloc, method, region); } template T D2DY2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + if (f.hasParallelSlices()) { ASSERT1(f.getDirectionY() == YDirectionType::Standard); return standardDerivative( @@ -233,7 +248,7 @@ T D2DY2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T D4DY4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + if (f.hasParallelSlices()) { ASSERT1(f.getDirectionY() == YDirectionType::Standard); return standardDerivative( @@ -251,14 +266,20 @@ T D4DY4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T DDZ(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } +inline Field3D DDZ(const Field3DParallel& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY") { + return DDZ(f.asField3D(), outloc, method, region); +} + template T D2DZ2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } @@ -266,7 +287,7 @@ T D2DZ2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T D4DZ4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } @@ -291,14 +312,14 @@ T D4DZ4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T VDDX(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return flowDerivative(vel, f, outloc, method, region); } template T FDDX(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return flowDerivative(vel, f, outloc, method, region); } @@ -307,7 +328,6 @@ T FDDX(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, template T VDDY(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); // Note the following chunk is copy+pasted from flowDerivative // above. Not pulled out as a separate function due the number of @@ -359,11 +379,16 @@ T VDDY(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, return are_unaligned ? fromFieldAligned(result, region) : result; } } +inline Field3D VDDY(const Field3D& v, const Field3DParallel& f, + CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY") { + return VDDY(v, f.asField3D(), outloc, method, region); +} template T FDDY(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + const bool fHasParallelSlices = (f.hasParallelSlices()); const bool velHasParallelSlices = (vel.hasParallelSlices()); if (fHasParallelSlices && velHasParallelSlices) { @@ -383,20 +408,25 @@ T FDDY(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, return are_unaligned ? fromFieldAligned(result, region) : result; } } +inline Field3D FDDY(const Field3D& v, const Field3DParallel& f, + CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY") { + return FDDY(v, f.asField3D(), outloc, method, region); +} ////////////// Z DERIVATIVE ///////////////// template T VDDZ(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return flowDerivative(vel, f, outloc, method, region); } template T FDDZ(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return flowDerivative(vel, f, outloc, method, region); } diff --git a/include/bout/interpolation.hxx b/include/bout/interpolation.hxx index 2c7df4472d..40ff603712 100644 --- a/include/bout/interpolation.hxx +++ b/include/bout/interpolation.hxx @@ -2,9 +2,9 @@ * Functions to interpolate between cell locations (e.g. lower Y and centred) * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -26,9 +26,19 @@ #ifndef BOUT_INTERP_H #define BOUT_INTERP_H +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" #include "bout/mesh.hxx" +#include "bout/msg_stack.hxx" +#include "bout/region.hxx" #include "bout/stencils.hxx" +#include +#include + /// Perform interpolation between centre -> shifted or vice-versa /*! Interpolate using 4th-order staggered formula @@ -55,15 +65,15 @@ inline BoutReal interp(const stencil& s) { @param[in] region Region where output will be calculated */ template -const T interp_to(const T& var, CELL_LOC loc, const std::string region = "RGN_ALL") { - AUTO_TRACE(); +std::enable_if_t || bout::utils::is_Field3D_v, const T> +interp_to(const T& var, CELL_LOC loc, const std::string& region = "RGN_ALL") { static_assert(bout::utils::is_Field2D_v || bout::utils::is_Field3D_v, "interp_to must be templated with one of Field2D or Field3D."); ASSERT1(loc != CELL_DEFAULT); // doesn't make sense to interplote to CELL_DEFAULT Mesh* fieldmesh = var.getMesh(); - if ((loc != CELL_CENTRE) && (fieldmesh->StaggerGrids == false)) { + if ((loc != CELL_CENTRE) && !fieldmesh->StaggerGrids) { throw BoutException("Asked to interpolate, but StaggerGrids is disabled!"); } @@ -72,7 +82,7 @@ const T interp_to(const T& var, CELL_LOC loc, const std::string region = "RGN_AL return var; } - // NOTE: invalidateGuards() is called in Field3D::alloctate() if the data + // NOTE: invalidateGuards() is called in Field3D::allocate() if the data // block is not already allocated, so will be called here if // region==RGN_NOBNDRY T result{emptyFrom(var).setLocation(loc)}; @@ -203,4 +213,16 @@ const T interp_to(const T& var, CELL_LOC loc, const std::string region = "RGN_AL return result; } +template +std::enable_if_t && !bout::utils::is_Field3D_v, const Field3D> +interp_to(const E& expr, CELL_LOC loc, const std::string& rgn = "RGN_ALL") { + return interp_to(Field3D{expr}, loc, rgn); +} + +template +std::enable_if_t && !bout::utils::is_Field2D_v, const Field2D> +interp_to(const E& expr, CELL_LOC loc, const std::string& rgn = "RGN_ALL") { + return interp_to(Field2D{expr}, loc, rgn); +} + #endif // BOUT_INTERP_H diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index 52dc38f174..1a8be9ef6b 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -1,8 +1,7 @@ /************************************************************************** - * Copyright 2010-2020 B.D.Dudson, S.Farley, P. Hill, J.T. Omotani, J.T. Parker, - * M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -24,9 +23,23 @@ #ifndef BOUT_INTERP_XZ_H #define BOUT_INTERP_XZ_H -#include "bout/mask.hxx" +#include +#include +#include +#include + +#if BOUT_HAS_PETSC +#include "bout/petsclib.hxx" +#endif + +namespace bout { +namespace details { +enum class implementation_type { new_weights, petsc, legacy }; +} +} // namespace bout class Options; +class GlobalField3DAccess; /// Interpolate a field onto a perturbed set of points const Field3D interpolate(const Field3D& f, const Field3D& delta_x, @@ -43,8 +56,7 @@ public: protected: Mesh* localmesh{nullptr}; - std::string region_name; - std::shared_ptr> region{nullptr}; + int region_id{-1}; public: XZInterpolation(int y_offset = 0, Mesh* localmeshIn = nullptr) @@ -52,48 +64,44 @@ public: localmesh(localmeshIn == nullptr ? bout::globals::mesh : localmeshIn) {} XZInterpolation(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) : XZInterpolation(y_offset, mesh) { - region = regionFromMask(mask, localmesh); + setMask(mask); } XZInterpolation(const std::string& region_name, int y_offset = 0, Mesh* mesh = nullptr) - : y_offset(y_offset), localmesh(mesh), region_name(region_name) {} - XZInterpolation(std::shared_ptr> region, int y_offset = 0, - Mesh* mesh = nullptr) - : y_offset(y_offset), localmesh(mesh), region(std::move(region)) {} + : y_offset(y_offset), localmesh(mesh), + region_id(localmesh->getRegionID(region_name)) {} + XZInterpolation(const Region& region, int y_offset = 0, Mesh* mesh = nullptr) + : y_offset(y_offset), localmesh(mesh) { + setRegion(region); + } virtual ~XZInterpolation() = default; - void setMask(const BoutMask& mask) { - region = regionFromMask(mask, localmesh); - region_name = ""; - } + void setMask(const BoutMask& mask) { setRegion(regionFromMask(mask, localmesh)); } void setRegion(const std::string& region_name) { - this->region_name = region_name; - this->region = nullptr; - } - void setRegion(const std::shared_ptr>& region) { - this->region_name = ""; - this->region = region; + this->region_id = localmesh->getRegionID(region_name); } + void setRegion(const std::unique_ptr> region) { setRegion(*region); } void setRegion(const Region& region) { - this->region_name = ""; - this->region = std::make_shared>(region); + std::string name; + int i = 0; + do { + name = fmt::format("unsec_reg_xz_interp_{:d}", i++); + } while (localmesh->hasRegion3D(name)); + localmesh->addRegion(name, region); + this->region_id = localmesh->getRegionID(name); } - Region getRegion() const { - if (!region_name.empty()) { - return localmesh->getRegion(region_name); - } - ASSERT1(region != nullptr); - return *region; + const Region& getRegion() const { + ASSERT2(region_id != -1); + return localmesh->getRegion(region_id); } - Region getRegion(const std::string& region) const { - const bool has_region = !region_name.empty() or this->region != nullptr; - if (!region.empty() and region != "RGN_ALL") { - if (has_region) { - return intersection(localmesh->getRegion(region), getRegion()); - } + const Region& getRegion(const std::string& region) const { + if (region_id == -1) { return localmesh->getRegion(region); } - ASSERT1(has_region); - return getRegion(); + if (region == "" or region == "RGN_ALL") { + return getRegion(); + } + return localmesh->getRegion( + localmesh->getCommonRegion(localmesh->getRegionID(region), region_id)); } virtual void calcWeights(const Field3D& delta_x, const Field3D& delta_z, const std::string& region = "RGN_NOBNDRY") = 0; @@ -129,13 +137,25 @@ public: } }; -class XZHermiteSpline : public XZInterpolation { +/// Monotonic Hermite spline interpolator +/// +/// Similar to XZHermiteSpline, so uses most of the same code. +/// Forces the interpolated result to be in the range of the +/// neighbouring cell values. This prevents unphysical overshoots, +/// but also degrades accuracy near maxima and minima. +/// Perhaps should only impose near boundaries, since that is where +/// problems most obviously occur. +template +class XZHermiteSplineBase : public XZInterpolation { protected: /// This is protected rather than private so that it can be /// extended and used by HermiteSplineMonotonic - Tensor i_corner; // x-index of bottom-left grid point - Tensor k_corner; // z-index of bottom-left grid point + Tensor> i_corner; // index of bottom-left grid point + Tensor k_corner; // z-index of bottom-left grid point + + std::unique_ptr gf3daccess; + Tensor> g3dinds; // Basis functions for cubic Hermite spline interpolation // see http://en.wikipedia.org/wiki/Cubic_Hermite_spline @@ -152,12 +172,38 @@ protected: Field3D h10_z; Field3D h11_z; + std::vector newWeights; + +#if BOUT_HAS_PETSC + PetscLib* petsclib; + bool isInit{false}; + Mat petscWeights{}; + Vec rhs{}, result{}; +#endif + + /// Factors to allow for some wiggleroom + BoutReal abs_fac_monotonic{1e-2}; + BoutReal rel_fac_monotonic{1e-1}; + public: - XZHermiteSpline(Mesh* mesh = nullptr) : XZHermiteSpline(0, mesh) {} - XZHermiteSpline(int y_offset = 0, Mesh* mesh = nullptr); - XZHermiteSpline(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) - : XZHermiteSpline(y_offset, mesh) { - region = regionFromMask(mask, localmesh); + XZHermiteSplineBase(Mesh* mesh = nullptr, [[maybe_unused]] Options* options = nullptr) + : XZHermiteSplineBase(0, mesh, options) {} + XZHermiteSplineBase(int y_offset = 0, Mesh* mesh = nullptr, Options* options = nullptr); + XZHermiteSplineBase(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr, + Options* options = nullptr) + : XZHermiteSplineBase(y_offset, mesh, options) { + setRegion(regionFromMask(mask, localmesh)); + } + ~XZHermiteSplineBase() override { +#if BOUT_HAS_PETSC + if (isInit) { + MatDestroy(&petscWeights); + VecDestroy(&rhs); + VecDestroy(&result); + isInit = false; + delete petsclib; + } +#endif } void calcWeights(const Field3D& delta_x, const Field3D& delta_z, @@ -178,30 +224,29 @@ public: getWeightsForYApproximation(int i, int j, int k, int yoffset) override; }; -/// Monotonic Hermite spline interpolator +using XZMonotonicHermiteSplineSerial = + XZHermiteSplineBase; +using XZHermiteSplineSerial = + XZHermiteSplineBase; +using XZMonotonicHermiteSplineLegacy = + XZHermiteSplineBase; +using XZHermiteSplineLegacy = + XZHermiteSplineBase; +#if BOUT_HAS_PETSC +using XZMonotonicHermiteSpline = + XZHermiteSplineBase; +using XZHermiteSpline = + XZHermiteSplineBase; +#else +using XZMonotonicHermiteSpline = + XZHermiteSplineBase; +using XZHermiteSpline = + XZHermiteSplineBase; +#endif + +/// XZLagrange4pt interpolation class /// -/// Similar to XZHermiteSpline, so uses most of the same code. -/// Forces the interpolated result to be in the range of the -/// neighbouring cell values. This prevents unphysical overshoots, -/// but also degrades accuracy near maxima and minima. -/// Perhaps should only impose near boundaries, since that is where -/// problems most obviously occur. -class XZMonotonicHermiteSpline : public XZHermiteSpline { -public: - XZMonotonicHermiteSpline(Mesh* mesh = nullptr) : XZHermiteSpline(0, mesh) {} - XZMonotonicHermiteSpline(int y_offset = 0, Mesh* mesh = nullptr) - : XZHermiteSpline(y_offset, mesh) {} - XZMonotonicHermiteSpline(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) - : XZHermiteSpline(mask, y_offset, mesh) {} - - using XZHermiteSpline::interpolate; - /// Interpolate using precalculated weights. - /// This function is called by the other interpolate functions - /// in the base class XZHermiteSpline. - Field3D interpolate(const Field3D& f, - const std::string& region = "RGN_NOBNDRY") const override; -}; - +/// Does not support MPI splitting in X class XZLagrange4pt : public XZInterpolation { Tensor i_corner; // x-index of bottom-left grid point Tensor k_corner; // z-index of bottom-left grid point @@ -209,11 +254,12 @@ class XZLagrange4pt : public XZInterpolation { Field3D t_x, t_z; public: - XZLagrange4pt(Mesh* mesh = nullptr) : XZLagrange4pt(0, mesh) {} + XZLagrange4pt(Mesh* mesh = nullptr, [[maybe_unused]] Options* options = nullptr) + : XZLagrange4pt(0, mesh) {} XZLagrange4pt(int y_offset = 0, Mesh* mesh = nullptr); XZLagrange4pt(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) : XZLagrange4pt(y_offset, mesh) { - region = regionFromMask(mask, localmesh); + setRegion(regionFromMask(mask, localmesh)); } void calcWeights(const Field3D& delta_x, const Field3D& delta_z, @@ -235,6 +281,9 @@ public: BoutReal lagrange_4pt(const BoutReal v[], BoutReal offset) const; }; +/// XZBilinear interpolation calss +/// +/// Does not support MPI splitting in X. class XZBilinear : public XZInterpolation { Tensor i_corner; // x-index of bottom-left grid point Tensor k_corner; // z-index of bottom-left grid point @@ -242,11 +291,12 @@ class XZBilinear : public XZInterpolation { Field3D w0, w1, w2, w3; public: - XZBilinear(Mesh* mesh = nullptr) : XZBilinear(0, mesh) {} + XZBilinear(Mesh* mesh = nullptr, [[maybe_unused]] Options* options = nullptr) + : XZBilinear(0, mesh) {} XZBilinear(int y_offset = 0, Mesh* mesh = nullptr); XZBilinear(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) : XZBilinear(y_offset, mesh) { - region = regionFromMask(mask, localmesh); + setRegion(regionFromMask(mask, localmesh)); } void calcWeights(const Field3D& delta_x, const Field3D& delta_z, @@ -266,7 +316,7 @@ public: }; class XZInterpolationFactory - : public Factory { + : public Factory { public: static constexpr auto type_name = "XZInterpolation"; static constexpr auto section_name = "xzinterpolation"; @@ -274,10 +324,10 @@ public: static constexpr auto default_type = "hermitespline"; ReturnType create(Options* options = nullptr, Mesh* mesh = nullptr) const { - return Factory::create(getType(options), mesh); + return Factory::create(getType(options), mesh, options); } - ReturnType create(const std::string& type, [[maybe_unused]] Options* options) const { - return Factory::create(type, nullptr); + ReturnType create(const std::string& type, Options* options) const { + return Factory::create(type, nullptr, options); } static void ensureRegistered(); diff --git a/include/bout/invert/laplacexy.hxx b/include/bout/invert/laplacexy.hxx index 5a4dd46512..51605b9db5 100644 --- a/include/bout/invert/laplacexy.hxx +++ b/include/bout/invert/laplacexy.hxx @@ -1,18 +1,18 @@ /************************************************************************** - * Laplacian solver in 2D (X-Y) + * Perpendicular Laplacian inversion in X-Y * - * Equation solved is: + * Equation solved is: * * Div( A * Grad_perp(x) ) + B*x = b * * Intended for use in solving n = 0 component of potential * from inversion of vorticity equation - * + * ************************************************************************** - * Copyright 2015 B.Dudson + * Copyright 2015-2025 BOUT++ Team * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -30,196 +30,67 @@ * **************************************************************************/ -#ifndef BOUT_LAPLACE_XY_H -#define BOUT_LAPLACE_XY_H - -#include "bout/build_defines.hxx" +#ifndef BOUT_LAPLACEXY_H +#define BOUT_LAPLACEXY_H -#if !BOUT_HAS_PETSC -// If no PETSc - -#warning LaplaceXY requires PETSc. No LaplaceXY available +#include +#include +#include +#include -#include +#include -class Field2D; -class Mesh; +class LaplaceXY; class Options; class Solver; -/*! - * Create a dummy class so that code will compile - * without PETSc, but will throw an exception if - * LaplaceXY is used. - */ -class LaplaceXY { +class LaplaceXYFactory + : public Factory { public: - LaplaceXY(Mesh* UNUSED(m) = nullptr, Options* UNUSED(opt) = nullptr, - const CELL_LOC UNUSED(loc) = CELL_CENTRE) { - throw BoutException("LaplaceXY requires PETSc. No LaplaceXY available"); - } - void setCoefs(const Field2D& UNUSED(A), const Field2D& UNUSED(B)) {} - const Field2D solve(const Field2D& UNUSED(rhs), const Field2D& UNUSED(x0)) { - throw BoutException("LaplaceXY requires PETSc. No LaplaceXY available"); + static constexpr auto type_name = "LaplaceXY"; + static constexpr auto section_name = "laplacexy"; + static constexpr auto option_name = "type"; + static constexpr auto default_type = "petsc"; + + ReturnType create(Mesh* mesh = nullptr, Options* options = nullptr, + CELL_LOC loc = CELL_CENTRE) const { + return Factory::create(getType(options), mesh, optionsOrDefaultSection(options), loc); } - void savePerformance(Solver&, std::string) { - throw BoutException("LaplaceXY requires PETSc. No LaplaceXY available"); + ReturnType create(const std::string& type, Options* options) const { + return Factory::create(type, nullptr, options, CELL_CENTRE); } + + static void ensureRegistered(); }; -#else // BOUT_HAS_PETSC +template +using RegisterLaplaceXY = LaplaceXYFactory::RegisterInFactory; -#include "bout/solver.hxx" -#include "bout/utils.hxx" -#include -#include -#include - -class Options; -class Solver; +using RegisterUnavailableLaplaceXY = LaplaceXYFactory::RegisterUnavailableInFactory; class LaplaceXY { public: - /*! - * Constructor - */ - LaplaceXY(Mesh* m = nullptr, Options* opt = nullptr, const CELL_LOC loc = CELL_CENTRE); - /*! - * Destructor - */ - ~LaplaceXY(); - - /*! - * Set coefficients (A, B) in equation: - * Div( A * Grad_perp(x) ) + B*x = b - */ - void setCoefs(const Field2D& A, const Field2D& B); - - /*! - * Solve Laplacian in X-Y - * - * Inputs - * ====== - * - * rhs - The field to be inverted. This must be allocated - * and contain valid data. - * x0 - Initial guess at the solution. If this is unallocated - * then an initial guess of zero will be used. - * - * Returns - * ======= - * - * The solution as a Field2D. On failure an exception will be raised - * - */ - const Field2D solve(const Field2D& rhs, const Field2D& x0); - - /*! - * Preconditioner function - * This is called by PETSc via a static function. - * and should not be called by external users - */ - int precon(Vec x, Vec y); - - /*! - * If this method is called, save some performance monitoring information - */ - void savePerformance(Solver& solver, const std::string& name = ""); - -private: - PetscLib lib; ///< Requires PETSc library - Mat MatA; ///< Matrix to be inverted - Vec xs, bs; ///< Solution and RHS vectors - KSP ksp; ///< Krylov Subspace solver - PC pc; ///< Preconditioner + LaplaceXY() = default; + LaplaceXY(const LaplaceXY&) = default; + LaplaceXY(LaplaceXY&&) = delete; + LaplaceXY& operator=(const LaplaceXY&) = default; + LaplaceXY& operator=(LaplaceXY&&) = delete; + virtual ~LaplaceXY() = default; - Mesh* localmesh; ///< The mesh this operates on, provides metrics and communication + virtual void setCoefs(const Field2D& A, const Field2D& B) = 0; - /// default prefix for writing performance logging variables - std::string default_prefix; + virtual Field2D solve(const Field2D& b, const Field2D& x0) = 0; - // Preconditioner - int xstart, xend; - int nloc, nsys; - Matrix acoef, bcoef, ccoef, xvals, bvals; - std::unique_ptr> cr; ///< Tridiagonal solver - - // Use finite volume or finite difference discretization - bool finite_volume{true}; - - // Y derivatives - bool include_y_derivs; // Include Y derivative terms? - - // Boundary conditions - bool x_inner_dirichlet; // Dirichlet on inner X boundary? - bool x_outer_dirichlet; // Dirichlet on outer X boundary? - std::string y_bndry{"neumann"}; // Boundary condition for y-boundary - - // Location of the rhs and solution - CELL_LOC location; - - /*! - * Number of grid points on this processor - */ - int localSize(); - - /*! - * Return the communicator for XY - */ - MPI_Comm communicator(); - - /*! - * Return the global index of a local (x,y) coordinate - * including guard cells. - * Boundary cells have a global index of -1 - * - * To do this, a Field2D (indexXY) is used to store - * the index as a floating point number which is then rounded - * to an integer. Guard cells are filled by communication - * so no additional logic is needed in Mesh. - */ - int globalIndex(int x, int y); - Field2D indexXY; ///< Global index (integer stored as BoutReal) - - // Save performance information? - bool save_performance = false; - - // Running average of number of iterations taken for solve in each output timestep - BoutReal average_iterations = 0.; - - // Variable to store the final result of average_iterations, since output is - // written after all other monitors have been called, and average_iterations - // must be reset in the monitor - BoutReal output_average_iterations = 0.; - - // Running total of number of calls to the solver in each output timestep - int n_calls = 0; - - // Utility methods - void setPreallocationFiniteVolume(PetscInt* d_nnz, PetscInt* o_nnz); - void setPreallocationFiniteDifference(PetscInt* d_nnz, PetscInt* o_nnz); - void setMatrixElementsFiniteVolume(const Field2D& A, const Field2D& B); - void setMatrixElementsFiniteDifference(const Field2D& A, const Field2D& B); - void solveFiniteVolume(const Field2D& x0); - void solveFiniteDifference(const Field2D& x0); - - // Monitor class used to reset performance-monitoring variables for a new - // output timestep - friend class LaplaceXYMonitor; - class LaplaceXYMonitor : public Monitor { - public: - LaplaceXYMonitor(LaplaceXY& owner) : laplacexy(owner) {} - - int call(Solver* /*solver*/, BoutReal /*time*/, int /*iter*/, int /*nout*/) override; - void outputVars(Options& output_options, const std::string& time_dimension) override; - - private: - // LaplaceXY object that this monitor belongs to - LaplaceXY& laplacexy; - }; + static std::unique_ptr create(Mesh* m = nullptr, Options* opt = nullptr, + CELL_LOC loc = CELL_CENTRE) { + return LaplaceXYFactory::getInstance().create(m, opt, loc); + } - LaplaceXYMonitor monitor; +protected: + static const int INVERT_DC_GRAD = 1; + static const int INVERT_AC_GRAD = 2; // Use zero neumann (NOTE: AC is a misnomer) + static const int INVERT_SET = 16; // Set boundary to x0 value + static const int INVERT_RHS = 32; // Set boundary to b value }; -#endif // BOUT_HAS_PETSC -#endif // BOUT_LAPLACE_XY_H +#endif // BOUT_LAPLACEXY_H diff --git a/include/bout/invert_laplace.hxx b/include/bout/invert_laplace.hxx index 9c3a153e20..76212dcd76 100644 --- a/include/bout/invert_laplace.hxx +++ b/include/bout/invert_laplace.hxx @@ -7,14 +7,14 @@ * \f[ * d \nabla^2_\perp x + (1/c)\nabla_\perp c\cdot\nabla_\perp x + a x = b * \f] - * + * * Where a, c and d are functions of x and y only (not z) */ /************************************************************************** * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -43,6 +43,7 @@ class Laplacian; #define PVEC_REAL_MPI_TYPE MPI_DOUBLE #endif +#include "bout/bout_types.hxx" #include "bout/field2d.hxx" #include "bout/field3d.hxx" #include "bout/fieldperp.hxx" @@ -53,6 +54,8 @@ class Laplacian; #include "bout/dcomplex.hxx" +class DSTTransform; +class FFTTransform; class Solver; constexpr auto LAPLACE_SPT = "spt"; @@ -141,7 +144,11 @@ public: static constexpr auto type_name = "Laplacian"; static constexpr auto section_name = "laplace"; static constexpr auto option_name = "type"; +#if BOUT_USE_METRIC_3D + static constexpr auto default_type = LAPLACE_PETSC; +#else static constexpr auto default_type = LAPLACE_CYCLIC; +#endif ReturnType create(Options* options = nullptr, CELL_LOC loc = CELL_CENTRE, Mesh* mesh = nullptr, Solver* solver = nullptr) { @@ -261,7 +268,7 @@ public: /// Coefficients in tridiagonal inversion void tridagCoefs(int jx, int jy, int jz, dcomplex& a, dcomplex& b, dcomplex& c, const Field2D* ccoef = nullptr, const Field2D* d = nullptr, - CELL_LOC loc = CELL_DEFAULT); + CELL_LOC loc = CELL_DEFAULT) const; /*! * Create a new Laplacian solver @@ -304,6 +311,10 @@ public: void savePerformance(Solver& solver, const std::string& name); protected: + // Give access for tridagMatrix + friend class DSTTransform; + friend class FFTTransform; + bool async_send; ///< If true, use asyncronous send in parallel algorithms int maxmode; ///< The maximum Z mode to solve for @@ -335,23 +346,24 @@ protected: void tridagCoefs(int jx, int jy, BoutReal kwave, dcomplex& a, dcomplex& b, dcomplex& c, const Field2D* ccoef = nullptr, const Field2D* d = nullptr, - CELL_LOC loc = CELL_DEFAULT) { + CELL_LOC loc = CELL_DEFAULT) const { tridagCoefs(jx, jy, kwave, a, b, c, ccoef, ccoef, d, loc); } void tridagCoefs(int jx, int jy, BoutReal kwave, dcomplex& a, dcomplex& b, dcomplex& c, const Field2D* c1coef, const Field2D* c2coef, const Field2D* d, - CELL_LOC loc = CELL_DEFAULT); + CELL_LOC loc = CELL_DEFAULT) const; void tridagMatrix(dcomplex* avec, dcomplex* bvec, dcomplex* cvec, dcomplex* bk, int jy, int kz, BoutReal kwave, const Field2D* a, const Field2D* ccoef, - const Field2D* d, bool includeguards = true, bool zperiodic = true) { + const Field2D* d, bool includeguards = true, + bool zperiodic = true) const { tridagMatrix(avec, bvec, cvec, bk, jy, kz, kwave, a, ccoef, ccoef, d, includeguards, zperiodic); } void tridagMatrix(dcomplex* avec, dcomplex* bvec, dcomplex* cvec, dcomplex* bk, int jy, int kz, BoutReal kwave, const Field2D* a, const Field2D* c1coef, const Field2D* c2coef, const Field2D* d, bool includeguards = true, - bool zperiodic = true); + bool zperiodic = true) const; CELL_LOC location; ///< staggered grid location of this solver Mesh* localmesh; ///< Mesh object for this solver Coordinates* coords; ///< Coordinates object, so we only have to call diff --git a/include/bout/invertable_operator.hxx b/include/bout/invertable_operator.hxx index fe139986be..d61c258654 100644 --- a/include/bout/invertable_operator.hxx +++ b/include/bout/invertable_operator.hxx @@ -42,7 +42,6 @@ class InvertableOperator; #include #include #include -#include #include #include #include @@ -61,14 +60,14 @@ namespace inversion { /// No-op function to use as a default -- may wish to remove once testing phase complete template T identity(const T& in) { - AUTO_TRACE(); + return in; }; /// Pack a PetscVec from a Field template PetscErrorCode fieldToPetscVec(const T& in, Vec out) { - TRACE("fieldToPetscVec"); + Timer timer("invertable_operator_packing"); PetscScalar* vecData; @@ -93,7 +92,7 @@ PetscErrorCode fieldToPetscVec(const T& in, Vec out) { /// Pack a Field from a PetscVec template PetscErrorCode petscVecToField(Vec in, T& out) { - TRACE("petscVecToField"); + Timer timer("invertable_operator_packing"); const PetscScalar* vecData; @@ -136,13 +135,12 @@ public: : operatorFunction(func), preconditionerFunction(func), opt(optIn == nullptr ? Options::getRoot()->getSection("invertableOperator") : optIn), - localmesh(localmeshIn == nullptr ? bout::globals::mesh : localmeshIn), lib(opt) { - AUTO_TRACE(); - }; + localmesh(localmeshIn == nullptr ? bout::globals::mesh : localmeshIn), lib(opt){ + + }; /// Destructor just has to cleanup the PETSc owned objects. ~InvertableOperator() { - TRACE("InvertableOperator::destructor"); KSPDestroy(&ksp); MatDestroy(&matOperator); @@ -157,7 +155,7 @@ public: /// do this they can set alsoSetPreconditioner to false. void setOperatorFunction(const function_signature& func, bool alsoSetPreconditioner = true) { - TRACE("InvertableOperator::setOperatorFunction"); + operatorFunction = func; if (alsoSetPreconditioner) { preconditionerFunction = func; @@ -166,28 +164,21 @@ public: /// Allow the user to override the existing preconditioner function void setPreconditionerFunction(const function_signature& func) { - TRACE("InvertableOperator::setPreconditionerFunction"); + preconditionerFunction = func; } /// Provide a way to apply the operator to a Field - T operator()(const T& input) { - TRACE("InvertableOperator::operator()"); - return operatorFunction(input); - } + T operator()(const T& input) { return operatorFunction(input); } /// Provide a synonym for applying the operator to a Field - T apply(const T& input) { - AUTO_TRACE(); - return operator()(input); - } + T apply(const T& input) { return operator()(input); } /// Sets up the PETSc objects required for inverting the operator /// Currently also takes the functor that applies the operator this class /// represents. Not actually required by any of the setup so this should /// probably be moved to a separate place (maybe the constructor). PetscErrorCode setup() { - TRACE("InvertableOperator::setup"); Timer timer("invertable_operator_setup"); if (doneSetup) { @@ -219,21 +210,18 @@ public: localmesh->LocalNy, localmesh->LocalNz, localmesh->maxregionblocksize); } } - if (localmesh->firstY() or localmesh->lastY()) { - for (int ix = localmesh->xstart; ix <= localmesh->xend; ix++) { - if (not localmesh->periodicY(ix)) { - if (localmesh->firstY()) { - nocorner3D += - Region(ix, ix, 0, localmesh->ystart - 1, 0, - localmesh->LocalNz - 1, localmesh->LocalNy, - localmesh->LocalNz, localmesh->maxregionblocksize); - } - if (localmesh->lastY()) { - nocorner3D += Region( - ix, ix, localmesh->LocalNy - localmesh->ystart, - localmesh->LocalNy - 1, 0, localmesh->LocalNz - 1, localmesh->LocalNy, - localmesh->LocalNz, localmesh->maxregionblocksize); - } + for (int ix = localmesh->xstart; ix <= localmesh->xend; ix++) { + if (not localmesh->periodicY(ix)) { + if (localmesh->firstY(ix)) { + nocorner3D += Region( + ix, ix, 0, localmesh->ystart - 1, 0, localmesh->LocalNz - 1, + localmesh->LocalNy, localmesh->LocalNz, localmesh->maxregionblocksize); + } + if (localmesh->lastY(ix)) { + nocorner3D += Region( + ix, ix, localmesh->LocalNy - localmesh->ystart, localmesh->LocalNy - 1, + 0, localmesh->LocalNz - 1, localmesh->LocalNy, localmesh->LocalNz, + localmesh->maxregionblocksize); } } } @@ -259,20 +247,17 @@ public: 0, 0, localmesh->LocalNy, 1, localmesh->maxregionblocksize); } } - if (localmesh->firstY() or localmesh->lastY()) { - for (int ix = localmesh->xstart; ix <= localmesh->xend; ix++) { - if (not localmesh->periodicY(ix)) { - if (localmesh->firstY()) { - nocorner2D += - Region(ix, ix, 0, localmesh->ystart - 1, 0, 0, - localmesh->LocalNy, 1, localmesh->maxregionblocksize); - } - if (localmesh->lastY()) { - nocorner2D += - Region(ix, ix, localmesh->LocalNy - localmesh->ystart, - localmesh->LocalNy - 1, 0, 0, localmesh->LocalNy, 1, - localmesh->maxregionblocksize); - } + for (int ix = localmesh->xstart; ix <= localmesh->xend; ix++) { + if (not localmesh->periodicY(ix)) { + if (localmesh->firstY(ix)) { + nocorner2D += + Region(ix, ix, 0, localmesh->ystart - 1, 0, 0, + localmesh->LocalNy, 1, localmesh->maxregionblocksize); + } + if (localmesh->lastY(ix)) { + nocorner2D += Region( + ix, ix, localmesh->LocalNy - localmesh->ystart, localmesh->LocalNy - 1, + 0, 0, localmesh->LocalNy, 1, localmesh->maxregionblocksize); } } } @@ -392,7 +377,7 @@ public: // but suspect it's not as there are KSPGuess objects // to deal with. T invert(const T& rhsField, const T& guess) { - AUTO_TRACE(); + auto ierr = fieldToPetscVec(guess, lhs); CHKERRQ(ierr); return invert(rhsField); @@ -403,7 +388,7 @@ public: /// of the operator we represent. Should probably provide an overload or similar as a /// way of setting the initial guess. T invert(const T& rhsField) { - TRACE("InvertableOperator::invert"); + Timer timer("invertable_operator_invert"); if (!doneSetup) { @@ -450,7 +435,6 @@ public: /// applying the registered function on the calculated inverse gives /// back the initial values. bool verify(const T& rhsIn, BoutReal tol = 1.0e-5) { - TRACE("InvertableOperator::verify"); T result = invert(rhsIn); localmesh->communicate(result); @@ -469,7 +453,7 @@ public: /// that as the Timer "labels" are not unique to an instance the time /// reported is summed across all different instances. static void reportTime() { - TRACE("InvertableOperator::reportTime"); + BoutReal time_setup = Timer::resetTime("invertable_operator_setup"); BoutReal time_invert = Timer::resetTime("invertable_operator_invert"); BoutReal time_packing = Timer::resetTime("invertable_operator_packing"); @@ -509,7 +493,7 @@ private: /// Copies data from v1 into a field of type T, calls the function on this and then /// copies the result into the v2 argument. static PetscErrorCode functionWrapper(Mat m, Vec v1, Vec v2) { - TRACE("InvertableOperator::functionWrapper"); + InvertableOperator* ctx; auto ierr = MatShellGetContext(m, &ctx); T tmpField(ctx->localmesh); @@ -540,7 +524,7 @@ private: /// Copies data from v1 into a field of type T, calls the function on this and then /// copies the result into the v2 argument. static PetscErrorCode preconditionerWrapper(Mat m, Vec v1, Vec v2) { - TRACE("InvertableOperator::functionWrapper"); + InvertableOperator* ctx; auto ierr = MatShellGetContext(m, &ctx); T tmpField(ctx->localmesh); @@ -575,7 +559,7 @@ public: }; #endif // PETSC -}; // namespace inversion -}; // namespace bout +}; // namespace inversion +}; // namespace bout #endif // HEADER GUARD diff --git a/include/bout/mask.hxx b/include/bout/mask.hxx index fd90ae7345..9033080d44 100644 --- a/include/bout/mask.hxx +++ b/include/bout/mask.hxx @@ -26,7 +26,7 @@ #include "bout/globals.hxx" #include "bout/mesh.hxx" -#include "bout/msg_stack.hxx" +#include "bout/region.hxx" /** * 3D array of bools to mask Field3Ds @@ -81,4 +81,12 @@ inline std::unique_ptr> regionFromMask(const BoutMask& mask, } return std::make_unique>(indices); } + +inline BoutMask maskFromRegion(const Region& region, const Mesh* mesh) { + BoutMask mask{mesh, false}; + + BOUT_FOR(i, region) { mask[i] = true; } + return mask; +} + #endif //BOUT_MASK_H diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index a1c88a2634..929e9d5aa7 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -3,7 +3,7 @@ * * Interface for mesh classes. Contains standard variables and useful * routines. - * + * * Changelog * ========= * @@ -11,16 +11,16 @@ * * Removing coordinate system into separate * Coordinates class * * Adding index derivative functions from derivs.cxx - * + * * 2010-06 Ben Dudson, Sean Farley * * Initial version, adapted from GridData class * * Incorporates code from topology.cpp and Communicator * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010-2025 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * - * Contact: Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -38,6 +38,7 @@ * **************************************************************************/ +#include "bout/boutexception.hxx" class Mesh; #ifndef BOUT_MESH_H @@ -58,16 +59,17 @@ class Mesh; #include "bout/sys/range.hxx" // RangeIterator #include "bout/unused.hxx" -#include "mpi.h" - #include #include #include #include #include +#include class BoundaryRegion; -class BoundaryRegionPar; +namespace bout::boundary { +class BoundaryRegionFCI; +} class GridDataSource; class MeshFactory : public Factory { @@ -262,6 +264,16 @@ public: communicate(g); } + /// Communicate Fields without calculating the parallel slices + template + void communicate_no_slices(Ts&... ts) { + FieldGroup g(ts...); + const bool old = calcParallelSlices_on_communicate; + calcParallelSlices_on_communicate = false; + communicate(g); + calcParallelSlices_on_communicate = old; + } + template void communicateXZ(Ts&... ts) { FieldGroup g(ts...); @@ -291,11 +303,6 @@ public: /// @param g The group of fields to communicate. Guard cells will be modified void communicateYZ(FieldGroup& g); - /*! - * Communicate an X-Z field - */ - virtual void communicate(FieldPerp& f); - /*! * Send a list of FieldData objects * Packs arguments into a FieldGroup and passes @@ -344,10 +351,14 @@ public: // non-local communications - virtual int getNXPE() = 0; ///< The number of processors in the X direction - virtual int getNYPE() = 0; ///< The number of processors in the Y direction - virtual int getXProcIndex() = 0; ///< This processor's index in X direction - virtual int getYProcIndex() = 0; ///< This processor's index in Y direction + virtual int getNXPE() const = 0; ///< The number of processors in the X direction + virtual int getNYPE() const = 0; ///< The number of processors in the Y direction + virtual int getNZPE() const = 0; ///< The number of processors in the Z direction + virtual int getXProcIndex() const = 0; ///< This processor's index in X direction + virtual int getYProcIndex() const = 0; ///< This processor's index in Y direction + virtual int getZProcIndex() const = 0; ///< This processor's index in Z direction + /// The index of a processor with given X, Y, and Z index + virtual int getProcIndex(int X, int Y, int Z) const = 0; // X communications virtual bool firstX() @@ -358,8 +369,6 @@ public: /// Domain is periodic in X? bool periodicX{false}; - int NXPE, PE_XIND; ///< Number of processors in X, and X processor index - /// Send a buffer of data to processor at X index +1 /// /// @param[in] buffer The data to send. Must be at least length \p size @@ -393,6 +402,7 @@ public: } ///< Return communicator containing all processors in X virtual MPI_Comm getXcomm(int jy) const = 0; ///< Return X communicator virtual MPI_Comm getYcomm(int jx) const = 0; ///< Return Y communicator + virtual MPI_Comm getXZcomm() const = 0; ///< Communicator in X-Z /// Return pointer to the mesh's MPI Wrapper object MpiWrapper& getMpi() { return *mpi; } @@ -464,21 +474,25 @@ public: /// Is there a boundary on the lower guard cells in Y /// on any processor along the X direction? - bool hasBndryLowerY(); + virtual bool hasBndryLowerY() const = 0; /// Is there a boundary on the upper guard cells in Y /// on any processor along the X direction? - bool hasBndryUpperY(); + virtual bool hasBndryUpperY() const = 0; // Boundary regions /// Return a vector containing all the boundary regions on this processor - virtual std::vector getBoundaries() = 0; + virtual std::vector getBoundaries() = 0; /// Get the set of all possible boundaries in this configuration - virtual std::set getPossibleBoundaries() const { return {}; } + virtual std::set getPossibleBoundaries() const { + throw BoutException("Not implemented for this mesh"); + }; /// Add a boundary region to this processor - virtual void addBoundary(BoundaryRegion* UNUSED(bndry)) {} + virtual void addBoundary(BoundaryRegionBase* UNUSED(bndry)) { + throw BoutException("This has never been implemented"); + }; /// Get the list of parallel boundary regions. The option specifies with /// region to get. Default is to get all regions. All possible options are @@ -488,20 +502,20 @@ public: /// mesh->getBoundariesPar(Mesh::BoundaryParType::all) /// get only xout: /// mesh->getBoundariesPar(Mesh::BoundaryParType::xout) - virtual std::vector> - getBoundariesPar(BoundaryParType type = BoundaryParType::all) = 0; + virtual std::vector> + getBoundariesPar(BoundaryParType type = BoundaryParType::all) const = 0; /// Add a parallel(Y) boundary to this processor - virtual void addBoundaryPar(std::shared_ptr UNUSED(bndry), - BoundaryParType UNUSED(type)) {} - - /// Branch-cut special handling (experimental) - virtual Field3D smoothSeparatrix(const Field3D& f) { return f; } + virtual void + addBoundaryPar(std::shared_ptr UNUSED(bndry), + BoundaryParType UNUSED(type)) {} virtual BoutReal GlobalX(int jx) const = 0; ///< Continuous X index between 0 and 1 virtual BoutReal GlobalY(int jy) const = 0; ///< Continuous Y index (0 -> 1) + virtual BoutReal GlobalZ(int jz) const = 0; ///< Continuous Z index (0 -> 1) virtual BoutReal GlobalX(BoutReal jx) const = 0; ///< Continuous X index between 0 and 1 virtual BoutReal GlobalY(BoutReal jy) const = 0; ///< Continuous Y index (0 -> 1) + virtual BoutReal GlobalZ(BoutReal jz) const = 0; ///< Continuous Z index (0 -> 1) ////////////////////////////////////////////////////////// @@ -626,10 +640,23 @@ public: return inserted.first->second; } + std::shared_ptr + getCoordinatesConst(const CELL_LOC location = CELL_CENTRE) const { + ASSERT1(location != CELL_DEFAULT); + ASSERT1(location != CELL_VSHIFT); + + auto found = coords_map.find(location); + if (found != coords_map.end()) { + // True branch most common, returns immediately + return found->second; + } + throw BoutException("Coordinates not yet set. Use non-const version!"); + } + /// Returns the non-CELL_CENTRE location /// allowed as a staggered location - CELL_LOC getAllowedStaggerLoc(DIRECTION direction) const { - AUTO_TRACE(); + static CELL_LOC getAllowedStaggerLoc(DIRECTION direction) { + switch (direction) { case (DIRECTION::X): return CELL_XLOW; @@ -647,7 +674,7 @@ public: /// Returns the number of grid points in the /// particular direction int getNpoints(DIRECTION direction) const { - AUTO_TRACE(); + switch (direction) { case (DIRECTION::X): return LocalNx; @@ -665,7 +692,7 @@ public: /// Returns the number of guard points in the /// particular direction int getNguard(DIRECTION direction) const { - AUTO_TRACE(); + switch (direction) { case (DIRECTION::X): return xstart; @@ -744,7 +771,7 @@ public: void addRegionPerp(const std::string& region_name, const Region& region); /// Converts an Ind2D to an Ind3D using calculation - Ind3D ind2Dto3D(const Ind2D& ind2D, int jz = 0) { + Ind3D ind2Dto3D(const Ind2D& ind2D, int jz = 0) const { return {ind2D.ind * LocalNz + jz, LocalNy, LocalNz}; } @@ -762,6 +789,12 @@ public: return {(indPerp.ind - jz) * LocalNy + LocalNz * jy + jz, LocalNy, LocalNz}; } + BOUT_HOST_DEVICE int flatIndPerpto3D(const int& flatIndPerp, const int nz, + int jy = 0) const { + int jz = flatIndPerp % nz; + return (flatIndPerp - jz) * LocalNy + LocalNz * jy + jz; + } + /// Converts an Ind3D to an Ind2D representing a 2D index using a lookup -- to be used with care Ind2D map3Dto2D(const Ind3D& ind3D) { return {indexLookup3Dto2D[ind3D.ind], LocalNy, 1}; @@ -782,15 +815,17 @@ protected: /// Mesh options section Options* options{nullptr}; +private: /// Set whether to call calcParallelSlices on all communicated fields (true) or not (false) bool calcParallelSlices_on_communicate{true}; +protected: /// Read a 1D array of integers const std::vector readInts(const std::string& name, int n); /// Calculates the size of a message for a given x and y range - int msg_len(const std::vector& var_list, int xge, int xlt, int yge, - int ylt); + int msg_len(const std::vector& var_list, int xge, int xlt, int yge, + int ylt) const; /// Initialise derivatives void derivs_init(Options* options); @@ -818,6 +853,16 @@ public: ASSERT1(RegionID.has_value()); return region3D[RegionID.value()]; } + bool isFci() const { + const auto coords = this->getCoordinatesConst(); + if (coords == nullptr) { + return false; + } + if (not coords->hasParallelTransform()) { + return false; + } + return not coords->getParallelTransform().canToFromFieldAligned(); + } private: /// Allocates default Coordinates objects @@ -827,8 +872,7 @@ private: /// (useful if CELL_CENTRE Coordinates have been changed, so reading from file /// would not be correct). std::shared_ptr - createDefaultCoordinates(const CELL_LOC location, - bool force_interpolate_from_centre = false); + createDefaultCoordinates(CELL_LOC location, bool force_interpolate_from_centre = false); //Internal region related information std::map regionMap3D; diff --git a/include/bout/msg_stack.hxx b/include/bout/msg_stack.hxx index 6d19469e25..aa19939058 100644 --- a/include/bout/msg_stack.hxx +++ b/include/bout/msg_stack.hxx @@ -31,27 +31,14 @@ class MsgStack; #include "bout/build_defines.hxx" -#include "bout/unused.hxx" - +#include "fmt/base.h" #include "fmt/core.h" -#include +#include #include #include #include -/// The __PRETTY_FUNCTION__ variable is defined by GCC (and some other families) but is -/// not a part of the standard. The __func__ variable *is* a part of the c++11 standard so -/// we'd like to fall back to this if possible. However as these are variables/constants -/// and not macros we can't just check if __PRETTY_FUNCITON__ is defined or not. Instead -/// we need to say if we support this or not by defining BOUT_HAS_PRETTY_FUNCTION (to be -/// implemented in configure) -#if BOUT_HAS_PRETTY_FUNCTION -#define __thefunc__ __PRETTY_FUNCTION__ -#else -#define __thefunc__ __func__ -#endif - /*! * Message stack * @@ -74,9 +61,10 @@ public: int push(std::string message); int push() { return push(""); } - template - int push(const S& format, const Args&... args) { - return push(fmt::format(format, args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + int push(fmt::format_string format, Args&&... args) { + return push(fmt::vformat(format, fmt::make_format_args(args...))); } void pop(); ///< Remove the last message @@ -88,19 +76,23 @@ public: #else /// Dummy functions which should be optimised out int push(const std::string&) { return 0; } - template - int push(const S&, const Args&...) { + template + int push([[maybe_unused]] fmt::format_string format, + [[maybe_unused]] const Args&... args) { return 0; } void pop() {} - void pop(int UNUSED(id)) {} + void pop([[maybe_unused]] int id) {} void clear() {} void dump() {} std::string getDump() { return ""; } #endif + /// Current stack size + std::size_t size() const { return position; } + private: std::vector stack; ///< Message stack; std::vector::size_type position{0}; ///< Position in stack @@ -143,10 +135,13 @@ public: MsgStackItem(const std::string& file, int line, const char* msg) : point(msg_stack.push("{:s} on line {:d} of '{:s}'", msg, line, file)) {} - template - MsgStackItem(const std::string& file, int line, const S& msg, const Args&... args) - : point(msg_stack.push("{:s} on line {:d} of '{:s}'", fmt::format(msg, args...), - line, file)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + MsgStackItem(const std::string& file, int line, fmt::format_string msg, + Args&&... args) + : point(msg_stack.push("{:s} on line {:d} of '{:s}'", + fmt::vformat(msg, fmt::make_format_args(args...)), line, + file)) {} ~MsgStackItem() { // If an exception has occurred, don't pop the message if (exception_count == std::uncaught_exceptions()) { @@ -193,23 +188,4 @@ private: #define TRACE(...) #endif -/*! - * The AUTO_TRACE macro provides a convenient way to put messages onto the msg_stack - * It pushes a message onto the stack, and pops it when the scope ends - * The message is automatically derived from the function signature - * as identified by the compiler. This will be PRETTY_FUNCTION if available - * else it will be the mangled form. - * - * This is implemented as a use of the TRACE macro with specific arguments. - * - * Example - * ------- - * - * { - * AUTO_TRACE(); - * - * } // Scope ends, message popped - */ -#define AUTO_TRACE() TRACE(__thefunc__) // NOLINT - #endif // BOUT_MSG_STACK_H diff --git a/include/bout/openmpwrap.hxx b/include/bout/openmpwrap.hxx index 00d0b1e5b7..19cc181787 100644 --- a/include/bout/openmpwrap.hxx +++ b/include/bout/openmpwrap.hxx @@ -4,7 +4,7 @@ * Copyright 2017 * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify diff --git a/include/bout/options.hxx b/include/bout/options.hxx index 9356326d0e..87503b0a22 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -12,7 +12,7 @@ * options and allows access to all sub-sections * ************************************************************************** -* Copyright 2010-2024 BOUT++ contributors +* Copyright 2010-2025 BOUT++ contributors * * Contact: Ben Dudson, dudson2@llnl.gov * @@ -39,6 +39,7 @@ class Options; #ifndef OPTIONS_H #define OPTIONS_H +#include "bout/array.hxx" #include "bout/bout_types.hxx" #include "bout/field2d.hxx" #include "bout/field3d.hxx" @@ -51,6 +52,7 @@ class Options; #include "bout/utils.hxx" #include +#include #include #include @@ -72,19 +74,19 @@ class Options; * which can be used as a map. * * Options options; - * + * * // Set values * options["key"] = 1.0; * * // Get values. Throws BoutException if not found - * int val = options["key"]; // Sets val to 1 + * int val = options["key"]; // Sets val to 1 * * // Return as specified type. Throws BoutException if not found * BoutReal var = options["key"].as(); * * // A default value can be used if key is not found * BoutReal value = options["pi"].withDefault(3.14); - * + * * // Assign value with source label. Throws if already has a value from same source * options["newkey"].assign(1.0, "some source"); * @@ -92,7 +94,7 @@ class Options; * options["newkey"].force(2.0, "some source"); * * A legacy interface is also supported: - * + * * options.set("key", 1.0, "code"); // Sets a key from source "code" * * int val; @@ -117,9 +119,9 @@ class Options; * * Each Options object can also contain any number of sections, which are * themselves Options objects. - * + * * Options §ion = options["section"]; - * + * * which can be nested: * * options["section"]["subsection"]["value"] = 3; @@ -132,13 +134,13 @@ class Options; * * e.g. * options->getSection("section")->getSection("subsection")->set("value", 3); - * + * * Options also know about their parents: * * Options &parent = section.parent(); - * + * * or - * + * * Options *parent = section->getParent(); * * Root options object @@ -148,8 +150,8 @@ class Options; * there is a global singleton Options object which can be accessed with a static function * * Options &root = Options::root(); - * - * or + * + * or * * Options *root = Options::getRoot(); * @@ -191,7 +193,7 @@ public: /// @param[in] parent Parent object /// @param[in] sectionName Name of the section, including path from the root Options(Options* parent_instance, std::string full_name) - : parent_instance(parent_instance), full_name(std::move(full_name)){}; + : parent_instance(parent_instance), full_name(std::move(full_name)) {}; /// Initialise with a value /// These enable Options to be constructed using initializer lists @@ -203,7 +205,16 @@ public: /// The type used to store values using ValueType = bout::utils::variant, Matrix, Tensor>; + Array, Array, Matrix, Matrix, + Tensor, Tensor>; + + /// Methods to iterate over `Options` + auto begin() { return std::begin(children); } + auto end() { return std::end(children); } + auto begin() const { return std::begin(children); } + auto end() const { return std::end(children); } + auto cbegin() const { return std::cbegin(children); } + auto cend() const { return std::cend(children); } /// A tree representation with leaves containing ValueType. /// Used to construct Options from initializer lists. @@ -430,6 +441,13 @@ public: return inputvalue; } + template + ResT operator=(const BinaryExpr& expr) { + ResT value{expr}; + assign(value); + return value; + } + /// Assign a value to the option. /// This will throw an exception if already has a value /// @@ -616,8 +634,8 @@ public: // Option not found. Copy the value from the default. this->_set_no_check(def.value, DEFAULT_SOURCE); - output_info << _("\tOption ") << full_name << " = " << def.full_name << " (" - << DEFAULT_SOURCE << ")\n"; + output_info.write("{}{} = {}({})\n", _("\tOption "), full_name, def.full_name, + DEFAULT_SOURCE); } else { // Check if this was previously set as a default option if (bout::utils::variantEqualTo(attributes.at("source"), DEFAULT_SOURCE)) { @@ -901,8 +919,8 @@ private: << ")\n"; } else { throw BoutException( - _("Options: Setting a value from same source ({:s}) to new value " - "'{:s}' - old value was '{:s}'."), + _f("Options: Setting a value from same source ({:s}) to new value " + "'{:s}' - old value was '{:s}'."), source, toString(val), bout::utils::variantToString(value)); } } @@ -955,9 +973,15 @@ Options& Options::assign<>(FieldPerp val, std::string source); template <> Options& Options::assign<>(Array val, std::string source); template <> +Options& Options::assign<>(Array val, std::string source); +template <> Options& Options::assign<>(Matrix val, std::string source); template <> +Options& Options::assign<>(Matrix val, std::string source); +template <> Options& Options::assign<>(Tensor val, std::string source); +template <> +Options& Options::assign<>(Tensor val, std::string source); /// Specialised similar comparison methods template <> @@ -967,29 +991,38 @@ inline bool Options::similar(BoutReal lhs, BoutReal rhs) const { /// Specialised as routines template <> -std::string Options::as(const std::string& similar_to) const; +auto Options::as(const std::string& similar_to) const -> std::string; +template <> +auto Options::as(const int& similar_to) const -> int; +template <> +auto Options::as(const BoutReal& similar_to) const -> BoutReal; +template <> +auto Options::as(const bool& similar_to) const -> bool; template <> -int Options::as(const int& similar_to) const; +auto Options::as(const Field2D& similar_to) const -> Field2D; template <> -BoutReal Options::as(const BoutReal& similar_to) const; +auto Options::as(const Field3D& similar_to) const -> Field3D; template <> -bool Options::as(const bool& similar_to) const; +auto Options::as(const FieldPerp& similar_to) const -> FieldPerp; template <> -Field2D Options::as(const Field2D& similar_to) const; +auto Options::as(const Array& similar_to) const -> Array; template <> -Field3D Options::as(const Field3D& similar_to) const; +auto Options::as(const Array& similar_to) const -> Array; template <> -FieldPerp Options::as(const FieldPerp& similar_to) const; +auto Options::as(const Matrix& similar_to) const -> Matrix; template <> -Array Options::as>(const Array& similar_to) const; +auto Options::as(const Matrix& similar_to) const -> Matrix; template <> -Matrix Options::as>(const Matrix& similar_to) const; +auto Options::as(const Tensor& similar_to) const -> Tensor; template <> -Tensor Options::as>(const Tensor& similar_to) const; +auto Options::as(const Tensor& similar_to) const -> Tensor; /// Convert \p value to string std::string toString(const Options& value); +/// Save the parallel fields +void saveParallel(Options& opt, const std::string& name, const Field3D& tosave); + /// Output a stringified \p value to a stream /// /// This is templated to avoid implict casting: anything is @@ -1021,7 +1054,40 @@ namespace details { /// so that we can put the function definitions in the .cxx file, /// avoiding lengthy recompilation if we change it struct OptionsFormatterBase { - auto parse(fmt::format_parse_context& ctx) -> fmt::format_parse_context::iterator; + constexpr auto parse(fmt::format_parse_context& ctx) { + const auto* it = ctx.begin(); + const auto* const end = ctx.end(); + + while (it != end and *it != '}') { + switch (*it) { + case 'd': + docstrings = true; + ++it; + break; + case 'i': + inline_section_names = true; + ++it; + break; + case 'k': + key_only = true; + ++it; + break; + case 's': + source = true; + ++it; + break; + case 'u': + unused = true; + ++it; + break; + default: + throw fmt::format_error("invalid format for 'Options'"); + } + } + + return it; + } + auto format(const Options& options, fmt::format_context& ctx) const -> fmt::format_context::iterator; @@ -1038,8 +1104,6 @@ private: bool key_only{false}; /// Include the 'source' attribute, if present bool source{false}; - /// Format string to passed down to subsections - std::string format_string; }; } // namespace details } // namespace bout diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index 48c791db22..81a21a62c5 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -43,8 +43,48 @@ #include #include +class Mesh; + namespace bout { +/// Parent class for IO to binary files and streams +/// +/// +/// Usage: +/// +/// 1. Dump files, containing time history: +/// +/// auto dump = OptionsIOFactory::getInstance().createOutput(); +/// dump->write(data); +/// +/// where data is an Options tree. By default dump files are configured +/// with the root `output` section, or an Option tree can be passed to +/// `createOutput`. +/// +/// 2. Restart files: +/// +/// auto restart = OptionsIOFactory::getInstance().createRestart(); +/// restart->write(data); +/// +/// where data is an Options tree. By default restart files are configured +/// with the root `restart_files` section, or an Option tree can be passed to +/// `createRestart`. +/// +/// 3. Ad-hoc single files +/// Note: The caller should consider how multiple processors interact with the file. +/// +/// auto file = OptionsIOFactory::getInstance().createFile("some_file.nc"); +/// or +/// auto file = OptionsIO::create("some_file.nc"); +/// +/// 4. Ad-hoc parallel files +/// This adds also metric information, such that the file can be read with +/// boutdata or xBOUT +/// +/// OptionIO::write("some_file", data, mesh); +/// +/// if mesh is omitted, no grid information is added. +/// class OptionsIO { public: /// No default constructor, as settings are required @@ -52,7 +92,7 @@ public: /// Constructor specifies the kind of file, and options to control /// the name of file, mode of operation etc. - OptionsIO(Options&) {} + OptionsIO(Options& /*unused*/) {} virtual ~OptionsIO() = default; @@ -74,10 +114,18 @@ public: /// ADIOS: Indicate completion of an output step. virtual void verifyTimesteps() const = 0; + /// NetCDF: Flush file to disk + virtual void flush() {} + /// Create an OptionsIO for I/O to the given file. /// This uses the default file type and default options. static std::unique_ptr create(const std::string& file); + /// Write some data to a file with a given name prefix + /// This will be done in parallel. If Mesh is given, also mesh data will be + /// added, which is needed for xBOUT or boutdata to read the files. + static void write(const std::string& prefix, Options& data, Mesh* mesh = nullptr); + /// Create an OptionsIO for I/O to the given file. /// The file will be configured using the given `config` options: /// - "type" : string The file type e.g. "netcdf" or "adios" diff --git a/include/bout/optionsreader.hxx b/include/bout/optionsreader.hxx index ee5a663945..ca9778b3c4 100644 --- a/include/bout/optionsreader.hxx +++ b/include/bout/optionsreader.hxx @@ -5,13 +5,13 @@ * different file formats * * Handles the command-line parsing -* +* */ /************************************************************************* * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu * * Contact: Ben Dudson, bd512@york.ac.uk -* +* * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -36,7 +36,8 @@ class OptionsReader; #include "bout/options.hxx" -#include "fmt/core.h" +#include +#include #include #include @@ -69,9 +70,10 @@ public: /// @param[in] file The name of the file. printf style arguments can be used to create the file name. void read(Options* options, const std::string& filename); - template - void read(Options* options, const S& format, const Args&... args) { - return read(options, fmt::format(format, args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void read(Options* options, fmt::format_string format, Args&&... args) { + return read(options, fmt::vformat(format, fmt::make_format_args(args...))); } /// Write options to file @@ -80,9 +82,10 @@ public: /// @param[in] file The name of the file to (over)write void write(Options* options, const std::string& filename); - template - void write(Options* options, const S& format, const Args&... args) { - return write(options, fmt::format(format, args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void write(Options* options, fmt::format_string format, Args&&... args) { + return write(options, fmt::vformat(format, fmt::make_format_args(args...))); } /// Parse options from the command line diff --git a/include/bout/output.hxx b/include/bout/output.hxx index a3a36635f3..1399f2cbd7 100644 --- a/include/bout/output.hxx +++ b/include/bout/output.hxx @@ -29,18 +29,20 @@ class Output; #ifndef BOUT_OUTPUT_H #define BOUT_OUTPUT_H -#include "bout/multiostream.hxx" #include -#include #include #include #include "bout/assert.hxx" -#include "bout/boutexception.hxx" -#include "bout/sys/gettext.hxx" // for gettext _() macro +#include "bout/build_defines.hxx" +#include "bout/multiostream.hxx" +#include "bout/sys/gettext.hxx" // IWYU pragma: keep for gettext _() macro #include "bout/unused.hxx" -#include "fmt/core.h" +#include +#include + +#include using std::endl; @@ -60,22 +62,29 @@ using std::endl; class Output : private multioutbuf_init>, public std::basic_ostream> { - using _Tr = std::char_traits; - using multioutbuf_init = ::multioutbuf_init; + using Tr = std::char_traits; + using multioutbuf_init = ::multioutbuf_init; public: - Output() : multioutbuf_init(), std::basic_ostream(multioutbuf_init::buf()) { + Output() : multioutbuf_init(), std::basic_ostream(multioutbuf_init::buf()) { Output::enable(); } + Output(const Output&) = delete; + Output(Output&&) = delete; + Output& operator=(const Output&) = delete; + Output& operator=(Output&&) = delete; + /// Specify a log file to open Output(const std::string& filename) { Output::enable(); open(filename); } - template - Output(const S& format, const Args&... args) : Output(fmt::format(format, args...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + Output(fmt::format_string format, Args&&... args) + : Output(fmt::vformat(format, fmt::make_format_args(args...))) {} ~Output() override { close(); } @@ -85,9 +94,10 @@ public: /// Open an output log file int open(const std::string& filename); - template - int open(const S& format, const Args&... args) { - return open(fmt::format(format, args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + int open(fmt::format_string format, Args&&... args) { + return open(fmt::vformat(format, fmt::make_format_args(args...))); } /// Close the log file @@ -96,25 +106,25 @@ public: /// Write a string using fmt format virtual void write(const std::string& message); - template - void write(const S& format, const Args&... args) { - write(fmt::format(format, args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void write(fmt::format_string format, Args&&... args) { + write(fmt::vformat(format, fmt::make_format_args(args...))); } /// Same as write, but only to screen virtual void print(const std::string& message); - template - void print(const S& format, const Args&... args) { - print(fmt::format(format, args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void print(fmt::format_string format, Args&&... args) { + print(fmt::vformat(format, fmt::make_format_args(args...))); } /// Add an output stream. All output will be sent to all streams - void add(std::basic_ostream& str) { multioutbuf_init::buf()->add(str); } + void add(std::basic_ostream& str) { multioutbuf_init::buf()->add(str); } /// Remove an output stream - void remove(std::basic_ostream& str) { - multioutbuf_init::buf()->remove(str); - } + void remove(std::basic_ostream& str) { multioutbuf_init::buf()->remove(str); } static Output* getInstance(); ///< Return pointer to instance @@ -125,7 +135,7 @@ protected: private: std::ofstream file; ///< Log file stream - bool enabled; ///< Whether output to stdout is enabled + bool enabled{}; ///< Whether output to stdout is enabled }; /// Class which behaves like Output, but has no effect. @@ -135,14 +145,14 @@ private: class DummyOutput : public Output { public: template - void write([[maybe_unused]] const S& format, [[maybe_unused]] const Args&... args){}; + void write([[maybe_unused]] const S& format, [[maybe_unused]] Args&&... args) {}; template - void print([[maybe_unused]] const S& format, [[maybe_unused]] const Args&... args){}; - void write([[maybe_unused]] const std::string& message) override{}; - void print([[maybe_unused]] const std::string& message) override{}; - void enable() override{}; - void disable() override{}; - void enable([[maybe_unused]] bool enable){}; + void print([[maybe_unused]] const S& format, [[maybe_unused]] Args&&... args) {}; + void write([[maybe_unused]] const std::string& message) override {}; + void print([[maybe_unused]] const std::string& message) override {}; + void enable() override {}; + void disable() override {}; + void enable([[maybe_unused]] bool enable) {}; bool isEnabled() override { return false; } }; @@ -155,24 +165,25 @@ class ConditionalOutput : public Output { public: /// @param[in] base The Output object which will be written to if enabled /// @param[in] enabled Should this be enabled by default? - ConditionalOutput(Output* base, bool enabled = true) : base(base), enabled(enabled){}; + ConditionalOutput(Output* base, bool enabled = true) : base(base), enabled(enabled) {}; /// Constuctor taking ConditionalOutput. This allows several layers of conditions /// /// @param[in] base A ConditionalOutput which will be written to if enabled /// - ConditionalOutput(ConditionalOutput* base) : base(base), enabled(base->enabled){}; + ConditionalOutput(ConditionalOutput* base) : base(base), enabled(base->enabled) {}; /// If enabled, writes a string using fmt formatting /// by calling base->write /// This string is then sent to log file and stdout (on processor 0) void write(const std::string& message) override; - template - void write(const S& format, const Args&... args) { + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void write(fmt::format_string format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->write(fmt::format(format, args...)); + base->write(fmt::vformat(format, fmt::make_format_args(args...))); } } @@ -180,11 +191,12 @@ public: /// note: unlike write, this is not also sent to log files void print(const std::string& message) override; - template - void print(const S& format, const Args&... args) { + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void print(fmt::format_string format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->print(fmt::format(format, args...)); + base->print(fmt::vformat(format, fmt::make_format_args(args...))); } } @@ -215,8 +227,6 @@ private: /// The lower-level Output to send output to Output* base; -protected: - friend class WithQuietOutput; /// Does this instance output anything? bool enabled; }; @@ -226,7 +236,7 @@ protected: /// output_debug << "debug message"; /// compile but have no effect if BOUT_USE_OUTPUT_DEBUG is false template -DummyOutput& operator<<(DummyOutput& out, T const& UNUSED(t)) { +DummyOutput& operator<<(DummyOutput& out, const T& UNUSED(t)) { return out; } @@ -250,7 +260,7 @@ inline ConditionalOutput& operator<<(ConditionalOutput& out, stream_manipulator } template -ConditionalOutput& operator<<(ConditionalOutput& out, T const& t) { +ConditionalOutput& operator<<(ConditionalOutput& out, const T& t) { if (out.isEnabled()) { *out.getBase() << t; } @@ -275,18 +285,23 @@ ConditionalOutput& operator<<(ConditionalOutput& out, const T* t) { /// // output now enabled class WithQuietOutput { public: - explicit WithQuietOutput(ConditionalOutput& output_in) : output(output_in) { - state = output.enabled; - output.disable(); + WithQuietOutput(const WithQuietOutput&) = delete; + WithQuietOutput(WithQuietOutput&&) = delete; + WithQuietOutput& operator=(const WithQuietOutput&) = delete; + WithQuietOutput& operator=(WithQuietOutput&&) = delete; + explicit WithQuietOutput(ConditionalOutput& output_in) + : output(&output_in), state(output->isEnabled()) { + output->disable(); } - ~WithQuietOutput() { output.enable(state); } + ~WithQuietOutput() { output->enable(state); } private: - ConditionalOutput& output; + ConditionalOutput* output; bool state; }; +// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) /// To allow statements like "output.write(...)" or "output << ..." /// Output for debugging #ifdef BOUT_USE_OUTPUT_DEBUG @@ -302,5 +317,6 @@ extern ConditionalOutput output_verbose; ///< less interesting messages /// Generic output, given the same level as output_progress extern ConditionalOutput output; +// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) #endif // BOUT_OUTPUT_H diff --git a/include/bout/output_bout_types.hxx b/include/bout/output_bout_types.hxx index b67762521b..77ea8ebf2d 100644 --- a/include/bout/output_bout_types.hxx +++ b/include/bout/output_bout_types.hxx @@ -5,10 +5,21 @@ #ifndef OUTPUT_BOUT_TYPES_H #define OUTPUT_BOUT_TYPES_H +#include "bout/bout_types.hxx" +#include "bout/mesh.hxx" +#include "bout/output.hxx" // IWYU pragma: keep +#include "bout/region.hxx" +#include "bout/traits.hxx" + +#include +#include #include -#include "bout/output.hxx" -#include "bout/region.hxx" +#include +#include +#include +#include +#include template struct fmt::formatter> { @@ -17,7 +28,8 @@ struct fmt::formatter> { // Parses format specifications of the form ['c' | 'i']. constexpr auto parse(format_parse_context& ctx) { - auto it = ctx.begin(), end = ctx.end(); + const auto* it = ctx.begin(); + const auto* end = ctx.end(); if (it != end && (*it == 'c' || *it == 'i')) { presentation = *it++; } @@ -50,4 +62,224 @@ struct fmt::formatter> { } }; +namespace bout { +namespace details { +template +/// Transpose a region so that it iterates in Z first, then Y, then X +/// +/// Caution: this is the most inefficient memory order! +auto region_transpose(const Region& region) -> Region; + +auto colour(BoutReal value, BoutReal min, BoutReal max) -> fmt::text_style; + +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +// +// Taken from fmt +// Copyright (c) 2012 - present, Victor Zverovich +// SPDX-License-Identifier: MIT +constexpr auto parse_nonnegative_int(const char*& begin, const char* end, + int error_value) noexcept -> int { + const auto* p = begin; + unsigned value = *p - '0'; + unsigned prev = 0; + ++p; + + while (p != end && '0' <= *p && *p <= '9') { + prev = value; + value = (value * 10) + unsigned(*p - '0'); + ++p; + } + auto num_digits = p - begin; + begin = p; + const auto digits10 = static_cast(sizeof(int) * CHAR_BIT * 3 / 10); + if (num_digits <= digits10) { + return static_cast(value); + } + // Check for overflow. + const unsigned max = INT_MAX; + return num_digits == digits10 + 1 and (prev * 10ULL) + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; +} +} // namespace details +} // namespace bout + +/// Formatter for Fields +/// +/// Format specification: +/// +/// - ``n``: Don't show indices +/// - ``r''``: Use given region (default: ``RGN_ALL``) +/// - ``T``: Transpose field so X is first dimension +/// - ``#``: Plot slices as 2D heatmap +/// - ``e``: Number of elements at each edge to show +/// - ``f``: Show full field +template +struct fmt::formatter, char>> { +private: + fmt::formatter underlying; + + static constexpr auto default_region = "RGN_ALL"; + std::string_view region = default_region; + + bool show_indices = true; + bool transpose = false; + bool plot = false; + int edgeitems = 4; + bool show_full = false; + +public: + constexpr auto parse(format_parse_context& ctx) { + const auto* it = ctx.begin(); + const auto* end = ctx.end(); + + if (it == end) { + return underlying.parse(ctx); + } + + while (it != end and *it != ':' and *it != '}') { + // Other cases handled explicitly below + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) + switch (*it) { + case 'e': + ++it; + edgeitems = bout::details::parse_nonnegative_int(it, end, -1); + if (edgeitems == -1) { + throw fmt::format_error("number is too big"); + } + break; + case 'f': + show_full = true; + ++it; + break; + case 'r': + ++it; + if (*it != '\'') { + throw fmt::format_error("invalid format for Field"); + } + { + const auto* rgn_start = ++it; + std::size_t size = 0; + while (*it != '\'') { + ++size; + ++it; + } + region = std::string_view(rgn_start, size); + } + ++it; + break; + case 'n': + show_indices = false; + ++it; + break; + case 'T': + transpose = true; + ++it; + break; + case '#': + plot = true; + show_indices = false; + ++it; + break; + } + } + + if (it != end and *it != '}') { + if (*it != ':') { + throw fmt::format_error("invalid format specifier"); + } + ++it; + } + + ctx.advance_to(it); + return underlying.parse(ctx); + } + + auto format(const T& f, format_context& ctx) const -> format_context::iterator { + using namespace bout::details; + + const auto* mesh = f.getMesh(); + + const auto rgn_str = std::string{region}; + const auto rgn_ = f.getRegion(rgn_str); + const auto rgn = transpose ? region_transpose(rgn_) : rgn_; + + const auto i = rgn.begin(); + int previous_x = i->x(); + int previous_y = i->y(); + int previous_z = i->z(); + + const auto last_i = rgn.getIndices().rbegin(); + const int last_x = last_i->x(); + const int last_y = last_i->y(); + const int last_z = last_i->z(); + + // Indices of edges + const int start_x = previous_x + edgeitems; + const int start_y = previous_y + edgeitems; + const int start_z = previous_z + edgeitems; + const int end_x = last_x - edgeitems; + const int end_y = last_y - edgeitems; + const int end_z = last_z - edgeitems; + + // Range of the data for plotting + BoutReal plot_min = 0.0; + BoutReal plot_max = 0.0; + if (plot) { + plot_min = min(f, false, rgn_str); + plot_max = max(f, false, rgn_str); + } + + // Separators + const auto block_sep = fmt::runtime("\n\n"); + const auto item_sep = fmt::runtime(plot ? "" : " "); + const auto x_sep = transpose ? item_sep : block_sep; + const auto z_sep = transpose ? block_sep : item_sep; + + // If we've shown the skip sep already this dim + bool shown_skip = false; + constexpr auto skip_sep = "..."; + + BOUT_FOR_SERIAL(i, rgn) { + const auto ix = i.x(); + const auto iy = i.y(); + const auto iz = i.z(); + + const bool should_show = + show_full + or ((ix < start_x or ix > end_x) and (iy < start_y or iy > end_y) + and (iz < start_z or iz > end_z)); + + if ((not shown_skip or should_show) and iz > previous_z) { + format_to(ctx.out(), z_sep); + } + if ((not shown_skip or should_show) and iy > previous_y) { + format_to(ctx.out(), "\n"); + } + if ((not shown_skip or should_show) and ix > previous_x) { + format_to(ctx.out(), x_sep); + } + + if (show_indices and should_show) { + format_to(ctx.out(), "{:c}: ", i); + } + if (plot) { + format_to(ctx.out(), "{}", styled("█", colour(f[i], plot_min, plot_max))); + } else if (should_show) { + underlying.format(f[i], ctx); + format_to(ctx.out(), ";"); + shown_skip = false; + } else if (not shown_skip) { + format_to(ctx.out(), skip_sep); + shown_skip = true; + } + previous_x = ix; + previous_y = iy; + previous_z = iz; + } + return format_to(ctx.out(), ""); + } +}; + #endif // OUTPUT_BOUT_TYPES_H diff --git a/include/bout/parallel_boundary_op.hxx b/include/bout/parallel_boundary_op.hxx index d8620e892b..5139cd5090 100644 --- a/include/bout/parallel_boundary_op.hxx +++ b/include/bout/parallel_boundary_op.hxx @@ -2,12 +2,18 @@ #define BOUT_PAR_BNDRY_OP_H #include "bout/boundary_op.hxx" +#include "bout/boundary_region_iter.hxx" #include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" +#include "bout/field3d.hxx" #include "bout/field_factory.hxx" #include "bout/parallel_boundary_region.hxx" +#include "bout/sys/expressionparser.hxx" #include "bout/unused.hxx" #include "bout/utils.hxx" +#include +#include #include ////////////////////////////////////////////////// @@ -16,30 +22,79 @@ class BoundaryOpPar : public BoundaryOpBase { public: BoundaryOpPar() = default; - BoundaryOpPar(BoundaryRegionPar* region, std::shared_ptr value) + BoundaryOpPar(bout::boundary::BoundaryRegionFCI* region, + std::shared_ptr value) : bndry(region), gen_values(std::move(value)), value_type(ValueType::GEN) {} - BoundaryOpPar(BoundaryRegionPar* region, Field3D* value) + BoundaryOpPar(bout::boundary::BoundaryRegionFCI* region, Field3D* value) : bndry(region), field_values(value), value_type(ValueType::FIELD) {} - BoundaryOpPar(BoundaryRegionPar* region, BoutReal value) + BoundaryOpPar(bout::boundary::BoundaryRegionFCI* region, BoutReal value) : bndry(region), real_value(value), value_type(ValueType::REAL) {} - BoundaryOpPar(BoundaryRegionPar* region) + BoundaryOpPar(bout::boundary::BoundaryRegionFCI* region) : bndry(region), real_value(0.), value_type(ValueType::REAL) {} + BoundaryOpPar(bout::boundary::BoundaryRegionX* region, + std::shared_ptr value) + : bndryX(region), gen_values(std::move(value)), value_type(ValueType::GEN) {} + BoundaryOpPar(bout::boundary::BoundaryRegionX* region, Field3D* value) + : bndryX(region), field_values(value), value_type(ValueType::FIELD) {} + BoundaryOpPar(bout::boundary::BoundaryRegionX* region, BoutReal value) + : bndryX(region), real_value(value) {} + BoundaryOpPar(bout::boundary::BoundaryRegionX* region) : bndryX(region) {} + BoundaryOpPar(bout::boundary::BoundaryRegionY* region, + std::shared_ptr value) + : bndryY(region), gen_values(std::move(value)), value_type(ValueType::GEN) {} + BoundaryOpPar(bout::boundary::BoundaryRegionY* region, Field3D* value) + : bndryY(region), field_values(value), value_type(ValueType::FIELD) {} + BoundaryOpPar(bout::boundary::BoundaryRegionY* region, BoutReal value) + : bndryY(region), real_value(value) {} + BoundaryOpPar(bout::boundary::BoundaryRegionY* region) : bndryY(region) {} + BoundaryOpPar(BoundaryOpPar* region, std::shared_ptr value) + : bndry(region->bndry), gen_values(std::move(value)), value_type(ValueType::GEN) {} + BoundaryOpPar(BoundaryOpPar* region, Field3D* value) + : bndry(region->bndry), field_values(value), value_type(ValueType::FIELD) {} + BoundaryOpPar(BoundaryOpPar* region, BoutReal value) + : bndry(region->bndry), real_value(value) {} + BoundaryOpPar(BoundaryOpPar* region) : bndry(region->bndry) {} ~BoundaryOpPar() override = default; // Note: All methods must implement clone, except for modifiers (see below) - virtual BoundaryOpPar* clone(BoundaryRegionPar* region, + virtual BoundaryOpPar* clone(bout::boundary::BoundaryRegionFCI* region, const std::list& args) = 0; - virtual BoundaryOpPar* clone(BoundaryRegionPar* region, Field3D* f) = 0; + virtual BoundaryOpPar* clone(bout::boundary::BoundaryRegionFCI* region, Field3D* f) = 0; virtual BoundaryOpPar* - clone(BoundaryRegionPar* region, const std::list& args, + clone(bout::boundary::BoundaryRegionFCI* region, const std::list& args, + const std::map& UNUSED(keywords)) { + return clone(region, args); + } + virtual BoundaryOpPar* clone(bout::boundary::BoundaryRegionX* region, + const std::list& args) = 0; + virtual BoundaryOpPar* clone(bout::boundary::BoundaryRegionX* region, Field3D* f) = 0; + virtual BoundaryOpPar* + clone(bout::boundary::BoundaryRegionX* region, const std::list& args, + const std::map& UNUSED(keywords)) { + return clone(region, args); + } + virtual BoundaryOpPar* clone(bout::boundary::BoundaryRegionY* region, + const std::list& args) = 0; + virtual BoundaryOpPar* clone(bout::boundary::BoundaryRegionY* region, Field3D* f) = 0; + virtual BoundaryOpPar* + clone(bout::boundary::BoundaryRegionY* region, const std::list& args, + const std::map& UNUSED(keywords)) { + return clone(region, args); + } + virtual BoundaryOpPar* clone(BoundaryOpPar* region, + const std::list& args) = 0; + virtual BoundaryOpPar* clone(BoundaryOpPar* region, Field3D* f) = 0; + virtual BoundaryOpPar* + clone(BoundaryOpPar* region, const std::list& args, const std::map& UNUSED(keywords)) { - // If not implemented, call two-argument version return clone(region, args); } - BoundaryRegionPar* bndry{nullptr}; +private: + bout::boundary::BoundaryRegionFCI* bndry{nullptr}; + bout::boundary::BoundaryRegionX* bndryX{nullptr}; + bout::boundary::BoundaryRegionY* bndryY{nullptr}; -protected: /// Possible ways to get boundary values std::shared_ptr gen_values; Field3D* field_values{nullptr}; @@ -49,7 +104,12 @@ protected: enum class ValueType { GEN, FIELD, REAL }; const ValueType value_type{ValueType::REAL}; - BoutReal getValue(const BoundaryRegionPar& bndry, BoutReal t); + BoutReal getValue(const bout::boundary::BoundaryRegionIterFCI& bndry, BoutReal t); + BoutReal getValue(const bout::boundary::BoundaryRegionIterX& bndry, BoutReal t); + BoutReal getValue(const bout::boundary::BoundaryRegionIterY& bndry, BoutReal t); + + template + friend class BoundaryOpParTemp; }; template @@ -59,8 +119,63 @@ public: using BoundaryOpPar::clone; - // Note: All methods must implement clone, except for modifiers (see below) - BoundaryOpPar* clone(BoundaryRegionPar* region, + BoundaryOpPar* clone(BoundaryOpPar* region, + const std::list& args) override { + if (!args.empty()) { + try { + real_value = stringToReal(args.front()); + return new T(region, real_value); + } catch (const BoutException&) { + std::shared_ptr newgen = nullptr; + // First argument should be an expression + newgen = FieldFactory::get()->parse(args.front()); + return new T(region, newgen); + } + } + + return new T(region); + } + BoundaryOpPar* clone(bout::boundary::BoundaryRegionFCI* region, + const std::list& args) override { + if (!args.empty()) { + try { + real_value = stringToReal(args.front()); + return new T(region, real_value); + } catch (const BoutException&) { + std::shared_ptr newgen = nullptr; + // First argument should be an expression + newgen = FieldFactory::get()->parse(args.front()); + return new T(region, newgen); + } + } + + return new T(region); + } + + BoundaryOpPar* clone(bout::boundary::BoundaryRegionFCI* region, Field3D* f) override { + return new T(region, f); + } + BoundaryOpPar* clone(bout::boundary::BoundaryRegionX* region, + const std::list& args) override { + if (!args.empty()) { + try { + real_value = stringToReal(args.front()); + return new T(region, real_value); + } catch (const BoutException&) { + std::shared_ptr newgen = nullptr; + // First argument should be an expression + newgen = FieldFactory::get()->parse(args.front()); + return new T(region, newgen); + } + } + + return new T(region); + } + + BoundaryOpPar* clone(bout::boundary::BoundaryRegionX* region, Field3D* f) override { + return new T(region, f); + } + BoundaryOpPar* clone(bout::boundary::BoundaryRegionY* region, const std::list& args) override { if (!args.empty()) { try { @@ -77,7 +192,10 @@ public: return new T(region); } - BoundaryOpPar* clone(BoundaryRegionPar* region, Field3D* f) override { + BoundaryOpPar* clone(bout::boundary::BoundaryRegionY* region, Field3D* f) override { + return new T(region, f); + } + BoundaryOpPar* clone(BoundaryOpPar* region, Field3D* f) override { return new T(region, f); } @@ -91,16 +209,38 @@ public: void apply(Field3D& f) override { return apply(f, 0); } void apply(Field3D& f, BoutReal t) override { - f.ynext(bndry->dir).allocate(); // Ensure unique before modifying - - auto dy = f.getCoordinates()->dy; - - for (bndry->first(); !bndry->isDone(); bndry->next()) { - BoutReal value = getValue(*bndry, t); - if (isNeumann) { - value *= dy[bndry->ind()]; + if (bndry != nullptr) { + f.ynext(bndry->dir()).allocate(); // Ensure unique before modifying + auto dy = f.getCoordinates()->dy; + for (auto pnt : *bndry) { + BoutReal value = getValue(pnt, t); + if (isNeumann) { + value *= dy[pnt.ind()]; + } + static_cast(this)->apply_stencil(f, pnt, value); + } + } + if (bndryX != nullptr) { + f.allocate(); + auto dy = f.getCoordinates()->dx; + for (auto pnt : *bndryX) { + BoutReal value = getValue(pnt, t); + if (isNeumann) { + value *= dy[pnt.ind()]; + } + static_cast(this)->apply_stencil(f, pnt, value); + } + } + if (bndryY != nullptr) { + f.allocate(); + auto dy = f.getCoordinates()->dy; + for (auto pnt : *bndryY) { + BoutReal value = getValue(pnt, t); + if (isNeumann) { + value *= dy[pnt.ind()]; + } + static_cast(this)->apply_stencil(f, pnt, value); } - static_cast(this)->apply_stencil(f, bndry, value); } } }; @@ -111,24 +251,27 @@ public: class BoundaryOpPar_dirichlet_o1 : public BoundaryOpParTemp { public: using BoundaryOpParTemp::BoundaryOpParTemp; - static void apply_stencil(Field3D& f, const BoundaryRegionPar* bndry, BoutReal value) { - bndry->dirichlet_o1(f, value); + template + static void apply_stencil(Field3D& f, T& pnt, BoutReal value) { + pnt.dirichlet_o1(f, value); } }; class BoundaryOpPar_dirichlet_o2 : public BoundaryOpParTemp { public: using BoundaryOpParTemp::BoundaryOpParTemp; - static void apply_stencil(Field3D& f, const BoundaryRegionPar* bndry, BoutReal value) { - bndry->dirichlet_o2(f, value); + template + static void apply_stencil(Field3D& f, T& pnt, BoutReal value) { + pnt.dirichlet_o2(f, value); } }; class BoundaryOpPar_dirichlet_o3 : public BoundaryOpParTemp { public: using BoundaryOpParTemp::BoundaryOpParTemp; - static void apply_stencil(Field3D& f, const BoundaryRegionPar* bndry, BoutReal value) { - bndry->dirichlet_o3(f, value); + template + static void apply_stencil(Field3D& f, T& pnt, BoutReal value) { + pnt.dirichlet_o3(f, value); } }; @@ -136,8 +279,9 @@ class BoundaryOpPar_neumann_o1 : public BoundaryOpParTemp { public: using BoundaryOpParTemp::BoundaryOpParTemp; - static void apply_stencil(Field3D& f, const BoundaryRegionPar* bndry, BoutReal value) { - bndry->neumann_o1(f, value); + template + static void apply_stencil(Field3D& f, T& pnt, BoutReal value) { + pnt.neumann_o1(f, value); } }; @@ -145,8 +289,9 @@ class BoundaryOpPar_neumann_o2 : public BoundaryOpParTemp { public: using BoundaryOpParTemp::BoundaryOpParTemp; - static void apply_stencil(Field3D& f, const BoundaryRegionPar* bndry, BoutReal value) { - bndry->neumann_o2(f, value); + template + static void apply_stencil(Field3D& f, T& pnt, BoutReal value) { + pnt.neumann_o2(f, value); } }; @@ -154,8 +299,9 @@ class BoundaryOpPar_neumann_o3 : public BoundaryOpParTemp { public: using BoundaryOpParTemp::BoundaryOpParTemp; - static void apply_stencil(Field3D& f, const BoundaryRegionPar* bndry, BoutReal value) { - bndry->neumann_o3(f, value); + template + static void apply_stencil(Field3D& f, T& pnt, BoutReal value) { + pnt.neumann_o3(f, value); } }; diff --git a/include/bout/parallel_boundary_region.hxx b/include/bout/parallel_boundary_region.hxx index 4d5278d00f..48668a5908 100644 --- a/include/bout/parallel_boundary_region.hxx +++ b/include/bout/parallel_boundary_region.hxx @@ -1,10 +1,22 @@ #ifndef BOUT_PAR_BNDRY_H #define BOUT_PAR_BNDRY_H +#include "bout/assert.hxx" #include "bout/boundary_region.hxx" +#include "bout/bout_enum_class.hxx" #include "bout/bout_types.hxx" +#include +#include +#include +#include +#include #include +#include "bout/build_defines.hxx" +#include "bout/field2d.hxx" +#include "bout/region.hxx" +#include "bout/sys/parallel_stencils.hxx" +#include "bout/utils.hxx" #include #include @@ -14,138 +26,209 @@ * */ -namespace parallel_stencil { -// generated by src/mesh/parallel_boundary_stencil.cxx.py -inline BoutReal pow(BoutReal val, int exp) { - // constexpr int expval = exp; - // static_assert(expval == 2 or expval == 3, "This pow is only for exponent 2 or 3"); - if (exp == 2) { - return val * val; - } - ASSERT3(exp == 3); - return val * val * val; -} -inline BoutReal dirichlet_o1(BoutReal UNUSED(spacing0), BoutReal value0) { - return value0; -} -inline BoutReal dirichlet_o2(BoutReal spacing0, BoutReal value0, BoutReal spacing1, - BoutReal value1) { - return (spacing0 * value1 - spacing1 * value0) / (spacing0 - spacing1); -} -inline BoutReal neumann_o2(BoutReal UNUSED(spacing0), BoutReal value0, BoutReal spacing1, - BoutReal value1) { - return -spacing1 * value0 + value1; -} -inline BoutReal dirichlet_o3(BoutReal spacing0, BoutReal value0, BoutReal spacing1, - BoutReal value1, BoutReal spacing2, BoutReal value2) { - return (pow(spacing0, 2) * spacing1 * value2 - pow(spacing0, 2) * spacing2 * value1 - - spacing0 * pow(spacing1, 2) * value2 + spacing0 * pow(spacing2, 2) * value1 - + pow(spacing1, 2) * spacing2 * value0 - spacing1 * pow(spacing2, 2) * value0) - / ((spacing0 - spacing1) * (spacing0 - spacing2) * (spacing1 - spacing2)); -} -inline BoutReal neumann_o3(BoutReal spacing0, BoutReal value0, BoutReal spacing1, - BoutReal value1, BoutReal spacing2, BoutReal value2) { - return (2 * spacing0 * spacing1 * value2 - 2 * spacing0 * spacing2 * value1 - + pow(spacing1, 2) * spacing2 * value0 - pow(spacing1, 2) * value2 - - spacing1 * pow(spacing2, 2) * value0 + pow(spacing2, 2) * value1) - / ((spacing1 - spacing2) * (2 * spacing0 - spacing1 - spacing2)); -} -} // namespace parallel_stencil +BOUT_ENUM_CLASS(SheathLimitMode, limit_free, exponential_free, linear_free); -class BoundaryRegionPar : public BoundaryRegionBase { +namespace bout { +namespace parallel_boundary_region { - struct RealPoint { - BoutReal s_x; - BoutReal s_y; - BoutReal s_z; - }; - - struct Indices { - // Indices of the boundary point - Ind3D index; - // Intersection with boundary in index space - RealPoint intersection; - // Distance to intersection - BoutReal length; - // Angle between field line and boundary - // BoutReal angle; - // How many points we can go in the opposite direction - signed char valid; - }; - - using IndicesVec = std::vector; - using IndicesIter = IndicesVec::iterator; +struct RealPoint { + BoutReal s_x; + BoutReal s_y; + BoutReal s_z; +}; - /// Vector of points in the boundary - IndicesVec bndry_points; - /// Current position in the boundary points - IndicesIter bndry_position; +struct Indices { + // Indices of the boundary point + Ind3D index; + // Intersection with boundary in index space + RealPoint intersection; + // Distance to intersection + BoutReal length; + // Angle between field line and boundary + // BoutReal angle; + // How many points we can go in the opposite direction + signed char valid; + signed char offset; + unsigned char abs_offset; + Indices(Ind3D index, RealPoint intersection, BoutReal length, signed char valid, + signed char offset, unsigned char abs_offset) + : index(index), intersection(intersection), length(length), valid(valid), + offset(offset), abs_offset(abs_offset) {}; +}; -public: - BoundaryRegionPar(const std::string& name, int dir, Mesh* passmesh) - : BoundaryRegionBase(name, passmesh), dir(dir) { - ASSERT0(std::abs(dir) == 1); - BoundaryRegionBase::isParallel = true; +inline bool operator<(const Indices& lhs, const Indices& rhs) { + return lhs.index < rhs.index; +} +inline bool operator<(const Indices& lhs, const Ind3D& rhs) { return lhs.index < rhs; } + +using IndicesVec = std::vector; +using IndicesIter = IndicesVec::iterator; +using IndicesIterConst = IndicesVec::const_iterator; + +/// Limited free gradient of log of a quantity +/// This ensures that the guard cell values remain positive +/// while also ensuring that the quantity never increases +/// +/// fm fc | fp +/// ^ boundary +/// +/// exp( 2*log(fc) - log(fm) ) +inline BoutReal limitFreeScale(BoutReal fm, BoutReal fc, SheathLimitMode mode) { + if ((fm < fc) && (mode == SheathLimitMode::limit_free)) { + return fc; // Neumann rather than increasing into boundary } - BoundaryRegionPar(const std::string& name, BndryLoc loc, int dir, Mesh* passmesh) - : BoundaryRegionBase(name, loc, passmesh), dir(dir) { - BoundaryRegionBase::isParallel = true; - ASSERT0(std::abs(dir) == 1); + if (fm < 1e-10) { + return fc; // Low / no density condition } - /// Add a point to the boundary - void add_point(Ind3D ind, BoutReal x, BoutReal y, BoutReal z, BoutReal length, - signed char valid) { - bndry_points.push_back({ind, {x, y, z}, length, valid}); + BoutReal fp = 0; + switch (mode) { + case SheathLimitMode::limit_free: + case SheathLimitMode::exponential_free: + fp = SQ(fc) / fm; // Exponential + break; + case SheathLimitMode::linear_free: + fp = (2.0 * fc) - fm; // Linear + break; } - void add_point(int ix, int iy, int iz, BoutReal x, BoutReal y, BoutReal z, - BoutReal length, signed char valid) { - bndry_points.push_back({xyz2ind(ix, iy, iz, localmesh), {x, y, z}, length, valid}); + +#if CHECKLEVEL >= 2 + if (!std::isfinite(fp)) { + throw BoutException("SheathBoundary limitFree: {}, {} -> {}", fm, fc, fp); } +#endif - // final, so they can be inlined - void first() final { bndry_position = begin(bndry_points); } - void next() final { ++bndry_position; } - bool isDone() final { return (bndry_position == end(bndry_points)); } + return fp; +} + +inline BoutReal limitFreeScale(BoutReal fm, BoutReal fc) { + if (fm < fc) { + return 1; // Neumann rather than increasing into boundary + } + if (fm < 1e-10) { + return 1; // Low / no density condition + } + BoutReal fp = fc / fm; +#if CHECKLEVEL >= 2 + if (!std::isfinite(fp)) { + throw BoutException("SheathBoundaryParallel limitFree: {}, {} -> {}", fm, fc, fp); + } +#endif + return fp; +} + +template +class BoundaryRegionParIterBase { + +public: + BoundaryRegionParIterBase(IndicesVec& bndry_points, IndicesIter bndry_position, int dir, + Mesh* localmesh) + : bndry_points(bndry_points), bndry_position(bndry_position), _dir(dir), + localmesh(localmesh) {}; // getter Ind3D ind() const { return bndry_position->index; } BoutReal s_x() const { return bndry_position->intersection.s_x; } BoutReal s_y() const { return bndry_position->intersection.s_y; } BoutReal s_z() const { return bndry_position->intersection.s_z; } - BoutReal length() const { return bndry_position->length; } + BoutReal length([[maybe_unused]] CELL_LOC loc) const { + ASSERT3(loc == CELL_CENTRE); + return bndry_position->length; + } signed char valid() const { return bndry_position->valid; } + signed char offset() const { return bndry_position->offset; } + unsigned char abs_offset() const { return bndry_position->abs_offset; } // setter - void setValid(signed char val) { bndry_position->valid = val; } + void setValid(signed char valid) { bndry_position->valid = valid; } - bool contains(const BoundaryRegionPar& bndry) const { - return std::binary_search( - begin(bndry_points), end(bndry_points), *bndry.bndry_position, - [](const Indices& i1, const Indices& i2) { return i1.index < i2.index; }); + // extrapolate a given point to the boundary + BoutReal extrapolate_sheath_o1(const Field3D& f) const { return ythis(f); } + BoutReal extrapolate_sheath_o2(const Field3D& f) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return extrapolate_sheath_o1(f); + } + return ythis(f) * (1 + length(f.getLocation())) - yprev(f) * length(f.getLocation()); + } + BoutReal + extrapolate_sheath_o1(const std::function& f) const { + return ythis(f); + } + BoutReal extrapolate_sheath_o2(const std::function& f, + CELL_LOC loc = CELL_CENTRE) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return extrapolate_sheath_o1(f); + } + return ythis(f) * (1 + length(loc)) - yprev(f) * length(loc); } - // extrapolate a given point to the boundary - BoutReal extrapolate_o1(const Field3D& f) const { return f[ind()]; } - BoutReal extrapolate_o2(const Field3D& f) const { + BoutReal interpolate_sheath_o2(const Field3D& f) const { + return ythis(f) * (1 - length(f.getLocation())) + ynext(f) * length(f.getLocation()); + } + BoutReal interpolate_sheath_o2(const std::function& f, + CELL_LOC loc = CELL_CENTRE) const { + return ythis(f) * (1 - length(loc)) + ynext(f) * length(loc); + } + + BoutReal extrapolate_next_o1(const Field3D& f) const { return ythis(f); } + BoutReal extrapolate_next_o2(const Field3D& f) const { ASSERT3(valid() >= 0); if (valid() < 1) { - return extrapolate_o1(f); + return extrapolate_next_o1(f); } - return f[ind()] * (1 + length()) - f.ynext(-dir)[ind().yp(-dir)] * length(); + return ythis(f) * 2 - yprev(f); } + BoutReal + extrapolate_next_o1(const std::function& f) const { + return ythis(f); + } + BoutReal + extrapolate_next_o2(const std::function& f) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return extrapolate_next_o1(f); + } + return ythis(f) * 2 - yprev(f); + } + + // extrapolate the gradient into the boundary + BoutReal extrapolate_grad_o1([[maybe_unused]] const Field3D& f) const { return 0; } + BoutReal extrapolate_grad_o2(const Field3D& f) const { + ASSERT3(valid() >= 0); + if (valid() < 1) { + return extrapolate_grad_o1(f); + } + return ythis(f) - ynext(f); + } + + BoundaryRegionParIterBase& operator*() { return *this; } + + BoundaryRegionParIterBase& operator++() { + ++bndry_position; + return *this; + } + + bool operator!=(const BoundaryRegionParIterBase& rhs) { + return bndry_position != rhs.bndry_position; + } + +#define ITER() for (int i = 0; i < localmesh->ystart - abs_offset(); ++i) // dirichlet boundary code void dirichlet_o1(Field3D& f, BoutReal value) const { - f.ynext(dir)[ind().yp(dir)] = value; + ITER() { getAt(f, i) = value; } } void dirichlet_o2(Field3D& f, BoutReal value) const { - if (length() < small_value) { + if (length(f.getLocation()) < small_value) { return dirichlet_o1(f, value); } - ynext(f) = parallel_stencil::dirichlet_o2(1, f[ind()], 1 - length(), value); - // ynext(f) = f[ind()] * (1 + 1/length()) + value / length(); + ITER() { + getAt(f, i) = parallel_stencil::dirichlet_o2( + i + 1, ythis(f), i + 1 - length(f.getLocation()), value); + } } void dirichlet_o3(Field3D& f, BoutReal value) const { @@ -153,18 +236,35 @@ public: if (valid() < 1) { return dirichlet_o2(f, value); } - if (length() < small_value) { - ynext(f) = parallel_stencil::dirichlet_o2(2, yprev(f), 1 - length(), value); + if (length(f.getLocation()) < small_value) { + ITER() { + getAt(f, i) = parallel_stencil::dirichlet_o2( + i + 2, yprev(f), i + 1 - length(f.getLocation()), value); + } } else { - ynext(f) = - parallel_stencil::dirichlet_o3(2, yprev(f), 1, f[ind()], 1 - length(), value); + ITER() { + getAt(f, i) = parallel_stencil::dirichlet_o3( + i + 2, yprev(f), i + 1, ythis(f), i + 1 - length(f.getLocation()), value); + } } } + void limit_at_least(Field3D& f, BoutReal value) const { + ITER() { + if (getAt(f, i) < value) { + getAt(f, i) = value; + } + } + } + + bool is_lower() const { return _dir == -1; } + // NB: value needs to be scaled by dy // neumann_o1 is actually o2 if we would use an appropriate one-sided stencil. // But in general we do not, and thus for normal C2 stencils, this is 1st order. - void neumann_o1(Field3D& f, BoutReal value) const { ynext(f) = f[ind()] + value; } + void neumann_o1(Field3D& f, BoutReal value) const { + ITER() { getAt(f, i) = ythis(f) + value * (i + 1); } + } // NB: value needs to be scaled by dy void neumann_o2(Field3D& f, BoutReal value) const { @@ -172,34 +272,229 @@ public: if (valid() < 1) { return neumann_o1(f, value); } - ynext(f) = yprev(f) + 2 * value; + ITER() { getAt(f, i) = yprev(f) + (2 + i) * value; } } // NB: value needs to be scaled by dy void neumann_o3(Field3D& f, BoutReal value) const { ASSERT3(valid() >= 0); if (valid() < 1) { - return neumann_o1(f, value); + return neumann_o2(f, value); + } + ITER() { + getAt(f, i) = parallel_stencil::neumann_o3(i + 1 - length(f.getLocation()), value, + i + 1, ythis(f), 2, yprev(f)); + } + } + + // extrapolate into the boundary using only monotonic decreasing values. + // f needs to be positive + void limitFree(Field3D& f) const { + const auto fac = valid() > 0 ? limitFreeScale(yprev(f), ythis(f)) : 1; + auto val = ythis(f); + ITER() { + val *= fac; + getAt(f, i) = val; } - ynext(f) = - parallel_stencil::neumann_o3(1 - length(), value, 1, f[ind()], 2, yprev(f)); } - const int dir; + BoutReal extrapolate_sheath_free(const Field3D& f, SheathLimitMode mode) const { + const auto fac = valid() > 0 ? limitFreeScale(yprev(f), ythis(f), mode) + : (mode == SheathLimitMode::linear_free ? 0 : 1); + auto val = ythis(f); + BoutReal next = mode == SheathLimitMode::linear_free ? val + fac : val * fac; + return val * length(f.getLocation()) + next * (1 - length(f.getLocation())); + } + + void set_free(Field3D& f, SheathLimitMode mode) const { + const auto fac = valid() > 0 ? limitFreeScale(yprev(f), ythis(f), mode) + : (mode == SheathLimitMode::linear_free ? 0 : 1); + auto val = ythis(f); + if (mode == SheathLimitMode::linear_free) { + ITER() { + val += fac; + getAt(f, i) = val; + } + } else { + ITER() { + val *= fac; + getAt(f, i) = val; + } + } + } + + void setAll(Field3D& f, const BoutReal val) const { + for (int i = -localmesh->ystart; i <= localmesh->ystart; ++i) { + f.ynext(i)[ind().yp(i)] = val; + } + } + + template + BoutReal& getAt(Field3D& f, int off) const { + ASSERT3(f.hasParallelSlices()); + if constexpr (check) { + ASSERT3(valid() > -off - 2); + } + auto _off = offset() + off * _dir; + return f.ynext(_off)[ind().yp(_off)]; + } + template + const BoutReal& getAt(const Field3D& f, int off) const { + ASSERT3(f.hasParallelSlices()); + if constexpr (check) { + ASSERT3(valid() > -off - 2); + } + auto _off = offset() + off * _dir; + return f.ynext(_off)[ind().yp(_off)]; + } + + const BoutReal& ynext(const Field3D& f) const { return getAt(f, 0); } + BoutReal& ynext(Field3D& f) const { return getAt(f, 0); } + const BoutReal& ythis(const Field3D& f) const { return getAt(f, -1); } + BoutReal& ythis(Field3D& f) const { return getAt(f, -1); } + const BoutReal& yprev(const Field3D& f) const { return getAt(f, -2); } + BoutReal& yprev(Field3D& f) const { return getAt(f, -2); } + + template + BoutReal getAt(const std::function& f, + int off) const { + if constexpr (check) { + ASSERT3(valid() > -off - 2); + } + auto _off = offset() + off * _dir; + return f(_off, ind().yp(_off)); + } + BoutReal ynext(const std::function& f) const { + return getAt(f, 0); + } + BoutReal ythis(const std::function& f) const { + return getAt(f, -1); + } + BoutReal yprev(const std::function& f) const { + return getAt(f, -2); + } + + void setYPrevIfValid(Field3D& f, BoutReal val) const { + if (valid() > 0) { + yprev(f) = val; + } + } + +#if not(BOUT_USE_METRIC_3D) + const BoutReal& ynext(const Field2D& f) const { return f.ynext(_dir)[ind().yp(_dir)]; } + BoutReal& ynext(Field2D& f) const { return f.ynext(_dir)[ind().yp(_dir)]; } + + const BoutReal& yprev(const Field2D& f) const { + ASSERT3(valid() > 0); + return f.ynext(-_dir)[ind().yp(-_dir)]; + } + BoutReal& yprev(Field2D& f) const { + ASSERT3(valid() > 0); + return f.ynext(-_dir)[ind().yp(-_dir)]; + } +#endif private: + const IndicesVec& bndry_points; + IndicesIter bndry_position; + constexpr static BoutReal small_value = 1e-2; + int _dir; + +public: + int dir() const { return _dir; } + Mesh* localmesh; +}; +} // namespace parallel_boundary_region +} // namespace bout +using BoundaryRegionParIter = bout::parallel_boundary_region::BoundaryRegionParIterBase< + bout::parallel_boundary_region::IndicesVec, + bout::parallel_boundary_region::IndicesIter>; +using BoundaryRegionParIterConst = + bout::parallel_boundary_region::BoundaryRegionParIterBase< + const bout::parallel_boundary_region::IndicesVec, + bout::parallel_boundary_region::IndicesIterConst>; + +class BoundaryRegionPar : public BoundaryRegionBase { +public: + BoundaryRegionPar(const std::string& name, int dir, Mesh* passmesh) + : BoundaryRegionBase(name, passmesh), _dir(dir) { + ASSERT0(std::abs(dir) == 1); + BoundaryRegionBase::isParallel = true; + } + BoundaryRegionPar(const std::string& name, BndryLoc loc, int dir, Mesh* passmesh) + : BoundaryRegionBase(name, loc, passmesh), _dir(dir) { + BoundaryRegionBase::isParallel = true; + ASSERT0(std::abs(dir) == 1); + } + + /// Add a point to the boundary + void add_point(Ind3D ind, BoutReal x, BoutReal y, BoutReal z, BoutReal length, + char valid, signed char offset) { + if (!bndry_points.empty() && bndry_points.back().index > ind) { + is_sorted = false; + } + bndry_points.emplace_back(ind, bout::parallel_boundary_region::RealPoint{x, y, z}, + length, valid, offset, + static_cast(std::abs(offset))); + } + void add_point(int ix, int iy, int iz, BoutReal x, BoutReal y, BoutReal z, + BoutReal length, char valid, signed char offset) { + add_point(xyz2ind(ix, iy, iz, localmesh), x, y, z, length, valid, offset); + } + + // final, so they can be inlined + void first() final { bndry_position = std::begin(bndry_points); } + void next() final { ++bndry_position; } + bool isDone() final { return (bndry_position == std::end(bndry_points)); } + + bool contains(const BoundaryRegionPar& bndry) const { + ASSERT2(is_sorted); + return std::binary_search(std::begin(bndry_points), std::end(bndry_points), + *bndry.bndry_position, + [](const bout::parallel_boundary_region::Indices& i1, + const bout::parallel_boundary_region::Indices& i2) { + return i1.index < i2.index; + }); + } + + bool contains(const int ix, const int iy, const int iz) const { + const auto i2 = xyz2ind(ix, iy, iz, localmesh); + return std::ranges::any_of(bndry_points.begin(), bndry_points.end(), + [&i2](auto i1) { return i1.index == i2; }); + } + + // setter + void setValid(char val) { bndry_position->valid = val; } + + // BoundaryRegionParIterConst begin() const { + // return BoundaryRegionParIterConst(bndry_points, bndry_points.begin(), dir); + // } + // BoundaryRegionParIterConst end() const { + // return BoundaryRegionParIterConst(bndry_points, bndry_points.begin(), dir); + // } + BoundaryRegionParIter begin() { + return BoundaryRegionParIter(bndry_points, bndry_points.begin(), _dir, localmesh); + } + BoundaryRegionParIter end() { + return BoundaryRegionParIter(bndry_points, bndry_points.end(), _dir, localmesh); + } + + int dir() const { return _dir; } + +private: + int _dir; + /// Vector of points in the boundary + bout::parallel_boundary_region::IndicesVec bndry_points; + /// Current position in the boundary points + bout::parallel_boundary_region::IndicesIter bndry_position; - // BoutReal get(const Field3D& f, int off) - const BoutReal& ynext(const Field3D& f) const { return f.ynext(dir)[ind().yp(dir)]; } - BoutReal& ynext(Field3D& f) const { return f.ynext(dir)[ind().yp(dir)]; } - const BoutReal& yprev(const Field3D& f) const { return f.ynext(-dir)[ind().yp(-dir)]; } - BoutReal& yprev(Field3D& f) const { return f.ynext(-dir)[ind().yp(-dir)]; } static Ind3D xyz2ind(int x, int y, int z, Mesh* mesh) { const int ny = mesh->LocalNy; const int nz = mesh->LocalNz; return Ind3D{(x * ny + y) * nz + z, ny, nz}; } + bool is_sorted{true}; }; #endif // BOUT_PAR_BNDRY_H diff --git a/include/bout/paralleltransform.hxx b/include/bout/paralleltransform.hxx index 49dea67743..d9ed397bb5 100644 --- a/include/bout/paralleltransform.hxx +++ b/include/bout/paralleltransform.hxx @@ -9,6 +9,7 @@ #include "bout/bout_types.hxx" #include "bout/dcomplex.hxx" #include "bout/field3d.hxx" +#include "bout/field_data.hxx" #include "bout/options.hxx" #include "bout/unused.hxx" @@ -90,6 +91,10 @@ public: /// require a twist-shift at branch cuts on closed field lines? virtual bool requiresTwistShift(bool twist_shift_enabled, YDirectionType ytype) = 0; + /// Can be implemented to load parallel metrics + /// Needed by FCI + virtual void loadParallelMetrics([[maybe_unused]] Coordinates* coords) {} + protected: /// This method should be called in the constructor to check that if the grid /// has a 'parallel_transform' variable, it has the correct value diff --git a/include/bout/petsc_interface.hxx b/include/bout/petsc_interface.hxx index 1378419872..830a7f4122 100644 --- a/include/bout/petsc_interface.hxx +++ b/include/bout/petsc_interface.hxx @@ -6,9 +6,9 @@ * up a linear system. * ************************************************************************** - * Copyright 2019 C. MacMackin + * Copyright 2019 - 2025 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -40,18 +40,27 @@ #include #include +#include +#include #include #include +#include #include #include +#include #include #include #include #include #include +#include +#include #include #include +#include + +#include /*! * A class which wraps PETSc vector objects, allowing them to be @@ -70,7 +79,7 @@ inline MPI_Comm getComm([[maybe_unused]] const T& field) { template <> inline MPI_Comm getComm([[maybe_unused]] const FieldPerp& field) { - return field.getMesh()->getXcomm(); + return field.getMesh()->getXZcomm(); } template @@ -284,7 +293,7 @@ public: PetscMatrix(IndexerPtr indConverter, bool preallocate = true) : matrix(new Mat()), indexConverter(indConverter), pt(&indConverter->getMesh()->getCoordinates()->getParallelTransform()) { - MPI_Comm comm = std::is_same_v ? indConverter->getMesh()->getXcomm() + MPI_Comm comm = std::is_same_v ? indConverter->getMesh()->getXZcomm() : BoutComm::get(); const int size = indexConverter->size(); @@ -346,7 +355,7 @@ public: ASSERT2(positions.size() == weights.size()); #if CHECK > 2 for (const auto val : weights) { - ASSERT3(finite(val)); + ASSERT3(std::isfinite(val)); } #endif if (positions.empty()) { @@ -363,29 +372,29 @@ public: } } Element& operator=(const Element& other) { - AUTO_TRACE(); + if (this == &other) { return *this; } - ASSERT3(finite(static_cast(other))); + ASSERT3(std::isfinite(static_cast(other))); *this = static_cast(other); return *this; } Element& operator=(BoutReal val) { - AUTO_TRACE(); - ASSERT3(finite(val)); + + ASSERT3(std::isfinite(val)); value = val; setValues(val, INSERT_VALUES); return *this; } Element& operator+=(BoutReal val) { - AUTO_TRACE(); - ASSERT3(finite(val)); + + ASSERT3(std::isfinite(val)); auto columnPosition = std::find(positions.begin(), positions.end(), petscCol); if (columnPosition != positions.end()) { const int index = std::distance(positions.begin(), columnPosition); value += weights[index] * val; - ASSERT3(finite(value)); + ASSERT3(std::isfinite(value)); } setValues(val, ADD_VALUES); return *this; @@ -394,7 +403,6 @@ public: private: void setValues(BoutReal val, InsertMode mode) { - TRACE("PetscMatrix setting values at ({}, {})", petscRow, petscCol); ASSERT3(positions.size() > 0); std::vector values; std::transform(weights.begin(), weights.end(), std::back_inserter(values), @@ -405,7 +413,8 @@ public: status = MatSetValues(*petscMatrix, 1, &petscRow, positions.size(), positions.data(), values.data(), mode); if (status != 0) { - throw BoutException("Error when setting elements of a PETSc matrix."); + throw BoutException("Error when setting elements of a PETSc matrix at ({}, {})", + petscRow, petscCol); } } Mat* petscMatrix; @@ -566,6 +575,26 @@ PetscVector operator*(const PetscMatrix& mat, const PetscVector& vec) { return PetscVector(vec, result); } +// Compatibility wrappers +// For < 3.24 +#if PETSC_VERSION_GE(3, 24, 0) \ + || (PETSC_VERSION_GE(3, 23, 0) && PETSC_VERSION_RELEASE == 0) +namespace bout { +template +constexpr auto cast_MatFDColoringFn(T func) { + return reinterpret_cast(func); // NOLINT(*-reinterpret-cast) +} +} // namespace bout +#else +using MatFDColoringFn = PetscErrorCode (*)(); +namespace bout { +template +constexpr auto cast_MatFDColoringFn(T func) { + return reinterpret_cast(func); // NOLINT(*-reinterpret-cast) +} +} // namespace bout +#endif + #endif // BOUT_HAS_PETSC #endif // BOUT_PETSC_INTERFACE_H diff --git a/include/bout/petsc_preconditioner.hxx b/include/bout/petsc_preconditioner.hxx new file mode 100644 index 0000000000..be283413fc --- /dev/null +++ b/include/bout/petsc_preconditioner.hxx @@ -0,0 +1,94 @@ +/************************************************************************** + * Reusable PETSc coloring-based Jacobian preconditioner support + * + * This class factors out the duplicated code in PETSc-based solvers for: + * - building a Jacobian sparsity pattern (via stencil assumptions) + * - creating a MatFDColoring context for efficient finite-difference Jacobians + * + **************************************************************************/ + +#ifndef BOUT_PETSC_PRECONDITIONER_H +#define BOUT_PETSC_PRECONDITIONER_H + +#include "bout/build_defines.hxx" + +#if BOUT_HAS_PETSC + +#include "bout/petsc_interface.hxx" +#include "bout/petsclib.hxx" + +#include + +#include +#include + +class Options; +class Field3D; + +class PetscPreconditioner { +public: + PetscPreconditioner() = default; + ~PetscPreconditioner() { reset(); } + + PetscPreconditioner(const PetscPreconditioner&) = delete; + PetscPreconditioner& operator=(const PetscPreconditioner&) = delete; + PetscPreconditioner(PetscPreconditioner&&) = delete; + PetscPreconditioner& operator=(PetscPreconditioner&&) = delete; + + /// Create and assemble a finite-difference Jacobian matrix `Jfd` with a sparsity + /// pattern derived from the solver's variable layout and a user-specified stencil. + /// + /// `index` should be generated by the calling solver (e.g. `Solver::globalIndex(0)`). + /// Safe to call multiple times; any existing PETSc objects are destroyed first. + PetscErrorCode createJacobianPattern(Field3D& index, Options& options, PetscInt nlocal, + int n2d, int n3d, MPI_Comm comm); + + /// Recalculate the PETSc coloring and finite-difference coloring context + /// for the currently stored `Jfd` matrix. + template + PetscErrorCode updateColoring(ColoringFunction function, void* ctx) { + // Re-calculate the coloring + MatColoring coloring{nullptr}; + PetscCall(MatColoringCreate(Jfd, &coloring)); + PetscCall(MatColoringSetType(coloring, MATCOLORINGGREEDY)); + PetscCall(MatColoringSetFromOptions(coloring)); + + // Calculate new index sets + ISColoring iscoloring{nullptr}; + PetscCall(MatColoringApply(coloring, &iscoloring)); + PetscCall(MatColoringDestroy(&coloring)); + + // Replace the old coloring with the new one + PetscCall(MatFDColoringDestroy(&fdcoloring)); + PetscCall(MatFDColoringCreate(Jfd, iscoloring, &fdcoloring)); + PetscCall( + MatFDColoringSetFunction(fdcoloring, bout::cast_MatFDColoringFn(function), ctx)); + PetscCall(MatFDColoringSetFromOptions(fdcoloring)); + PetscCall(MatFDColoringSetUp(Jfd, iscoloring, fdcoloring)); + PetscCall(ISColoringDestroy(&iscoloring)); + + PetscFunctionReturn(PETSC_SUCCESS); + } + + Mat jacobian() const { return Jfd; } + MatFDColoring coloring() const { return fdcoloring; } + + void reset(); + +private: + Mat Jfd{nullptr}; + MatFDColoring fdcoloring{nullptr}; +}; + +#else + +// PETSc is not available, but keep the type defined so headers can be included +// unconditionally in PETSc-enabled compilation units. +class PetscPreconditioner { +public: + void reset() {} +}; + +#endif // BOUT_HAS_PETSC + +#endif // BOUT_PETSC_PRECONDITIONER_H diff --git a/include/bout/petsclib.hxx b/include/bout/petsclib.hxx index 2008671286..41d7618fd2 100644 --- a/include/bout/petsclib.hxx +++ b/include/bout/petsclib.hxx @@ -22,9 +22,9 @@ * so it *must* be included before *any* PETSc header. * ************************************************************************** - * Copyright 2012 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2012 - 2025 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -60,6 +60,7 @@ class Options; #define PETSC_HAVE_BROKEN_RECURSIVE_MACRO #include // IWYU pragma: export +#include #include #include "bout/boutexception.hxx" @@ -113,6 +114,9 @@ public: /// was passed to the constructor. void setOptionsFromInputFile(SNES& snes); + /// Set options for a TS time integrator + void setOptionsFromInputFile(TS& ts); + /*! * Force cleanup. This will call PetscFinalize, printing a warning * if any instances of PetscLib still exist diff --git a/include/bout/physicsmodel.hxx b/include/bout/physicsmodel.hxx index 2a7618905f..f9160f5709 100644 --- a/include/bout/physicsmodel.hxx +++ b/include/bout/physicsmodel.hxx @@ -1,20 +1,20 @@ /*!************************************************************************ * \file physicsmodel.hxx - * + * * @brief Base class for Physics Models - * - * + * + * * * Changelog: - * + * * 2013-08 Ben Dudson * * Initial version - * + * ************************************************************************** * Copyright 2013 B.D.Dudson * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -47,6 +47,9 @@ class PhysicsModel; #include "bout/unused.hxx" #include "bout/utils.hxx" +#include +#include +#include #include #include @@ -164,9 +167,9 @@ public: * * Output * ------ - * + * * The time derivatives will be put in the ddt() variables - * + * * Returns a flag: 0 indicates success, non-zero an error flag */ int runRHS(BoutReal time, bool linear = false); @@ -200,7 +203,7 @@ public: bool hasPrecon(); /*! - * Run the preconditioner. The system state should be in the + * Run the preconditioner. The system state should be in the * evolving variables, and the vector to be solved in the ddt() variables. * The result will be put in the ddt() variables. * @@ -216,7 +219,7 @@ public: /*! * Run the Jacobian-vector multiplication function - * + * * Note: this is usually only called by the Solver */ int runJacobian(BoutReal t); @@ -238,10 +241,10 @@ protected: // The init and rhs functions are implemented by user code to specify problem /*! * @brief This function is called once by the solver at the start of a simulation. - * + * * A valid PhysicsModel must implement this function - * - * Variables should be read from the inputs, and the variables to + * + * Variables should be read from the inputs, and the variables to * be evolved should be specified. */ virtual int init(bool restarting) = 0; @@ -255,7 +258,7 @@ protected: /*! * @brief This function is called by the time integration solver * at least once per time step - * + * * Variables being evolved will be set by the solver * before the call, and this function must calculate * and set the time-derivatives. @@ -275,10 +278,10 @@ protected: /// Add additional variables other than the evolving variables to the restart files virtual void restartVars(Options& options); - /* + /* If split operator is set to true, then convective() and diffusive() are called instead of rhs() - + For implicit-explicit schemes, convective() will typically be treated explicitly, whilst diffusive() will be treated implicitly. For unsplit methods, both convective and diffusive will be called @@ -331,7 +334,7 @@ protected: * * @param[in] var The variable to evolve * @param[in] name The name to use for variable initialisation and output - * + * * Note that the variable must not be destroyed (e.g. go out of scope) * after this call, since a pointer to \p var is stored in the solver. * @@ -355,11 +358,11 @@ protected: * Specify a constrained variable \p var, which will be * adjusted to make \p F_var equal to zero. * If the solver does not support constraints then this will throw an exception - * + * * @param[in] var The variable the solver should modify * @param[in] F_var The control variable, which the user will set * @param[in] name The name to use for initialisation and output - * + * */ bool bout_constrain(Field3D& var, Field3D& F_var, const char* name); @@ -376,6 +379,9 @@ protected: PhysicsModel* model; }; + /// Set timestep counter for flushing file + void setFlushCounter(std::size_t iteration) { flush_counter = iteration; } + private: /// State for outputs Options output_options; @@ -399,6 +405,10 @@ private: bool initialised{false}; /// write restarts and pass outputMonitor method inside a Monitor subclass PhysicsModelMonitor modelMonitor{this}; + /// How often to flush to disk + std::size_t flush_frequency{1}; + /// Current timestep counter + std::size_t flush_counter{0}; }; /*! @@ -417,14 +427,14 @@ private: */ #define BOUTMAIN(ModelClass) \ int main(int argc, char** argv) { \ - int init_err = BoutInitialise(argc, argv); \ - if (init_err < 0) { \ - return 0; \ - } \ - if (init_err > 0) { \ - return init_err; \ - } \ try { \ + int init_err = BoutInitialise(argc, argv); \ + if (init_err < 0) { \ + return 0; \ + } \ + if (init_err > 0) { \ + return init_err; \ + } \ auto model = bout::utils::make_unique(); \ auto solver = Solver::create(); \ solver->setModel(model.get()); \ @@ -432,8 +442,8 @@ private: solver->addMonitor(bout_monitor.get(), Solver::BACK); \ solver->solve(); \ } catch (const BoutException& e) { \ - output << "Error encountered: " << e.what(); \ - output << e.getBacktrace() << endl; \ + output.write("Error encountered: {}\n", e.what()); \ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); \ MPI_Abort(BoutComm::get(), 1); \ } \ BoutFinalise(); \ @@ -480,8 +490,7 @@ private: /// Add fields to the solver. /// This should accept up to ten arguments -#define SOLVE_FOR(...) \ - { MACRO_FOR_EACH(SOLVE_FOR1, __VA_ARGS__) } +#define SOLVE_FOR(...) {MACRO_FOR_EACH(SOLVE_FOR1, __VA_ARGS__)} /// Write this variable once to the grid file #define SAVE_ONCE1(var) dump.addOnce(var, #var); @@ -521,8 +530,7 @@ private: dump.addOnce(var6, #var6); \ } -#define SAVE_ONCE(...) \ - { MACRO_FOR_EACH(SAVE_ONCE1, __VA_ARGS__) } +#define SAVE_ONCE(...) {MACRO_FOR_EACH(SAVE_ONCE1, __VA_ARGS__)} /// Write this variable every timestep #define SAVE_REPEAT1(var) dump.addRepeat(var, #var); @@ -562,7 +570,6 @@ private: dump.addRepeat(var6, #var6); \ } -#define SAVE_REPEAT(...) \ - { MACRO_FOR_EACH(SAVE_REPEAT1, __VA_ARGS__) } +#define SAVE_REPEAT(...) {MACRO_FOR_EACH(SAVE_REPEAT1, __VA_ARGS__)} #endif // BOUT_PHYSICS_MODEL_H diff --git a/include/bout/rajalib.hxx b/include/bout/rajalib.hxx index 83be632d48..859463325e 100644 --- a/include/bout/rajalib.hxx +++ b/include/bout/rajalib.hxx @@ -14,6 +14,7 @@ */ #pragma once +#include "bout/array.hxx" #ifndef RAJALIB_H #define RAJALIB_H @@ -23,6 +24,15 @@ #include "RAJA/RAJA.hpp" // using RAJA lib +#if BOUT_HAS_CUDA +// TODO: Make configurable +const int CUDA_BLOCK_SIZE = 256; +using EXEC_POL = RAJA::cuda_exec; +//using EXEC_POL = RAJA::loop_exec; +#else // not BOUT_USE_CUDA +using EXEC_POL = RAJA::loop_exec; +#endif // end BOUT_USE_CUDA + /// Wrapper around RAJA::forall /// Enables computations to be done on CPU or GPU (CUDA). /// @@ -81,7 +91,7 @@ struct RajaForAll { // Note: must be a local variable const int* _ob_i_ind_raw = &_ob_i_ind[0]; RAJA::forall(RAJA::RangeSegment(0, _ob_i_ind.size()), - [=] RAJA_DEVICE(int id) { + [=] RAJA_DEVICE(int id) mutable { // Look up index and call user function f(_ob_i_ind_raw[id]); }); @@ -127,7 +137,7 @@ private: /// to create variables which shadow the class members. /// #define BOUT_FOR_RAJA(index, region, ...) \ - RajaForAll(region) << [ =, ##__VA_ARGS__ ] RAJA_DEVICE(int index) + RajaForAll(region) << [ =, ##__VA_ARGS__ ] RAJA_DEVICE(int index) mutable #else // BOUT_HAS_RAJA diff --git a/include/bout/region.hxx b/include/bout/region.hxx index bb1cf82bf1..88829fc65e 100644 --- a/include/bout/region.hxx +++ b/include/bout/region.hxx @@ -49,6 +49,7 @@ #include #include +#include "bout/array.hxx" #include "bout/assert.hxx" #include "bout/bout_types.hxx" #include "bout/boutexception.hxx" @@ -139,7 +140,7 @@ class BoutMask; BOUT_FOR_OMP(index, (region), for schedule(BOUT_OPENMP_SCHEDULE) nowait) // NOLINTEND(cppcoreguidelines-macro-usage,bugprone-macro-parentheses) -enum class IND_TYPE { IND_3D = 0, IND_2D = 1, IND_PERP = 2 }; +enum class IND_TYPE { IND_3D = 0, IND_2D = 1, IND_PERP = 2, IND_GLOBAL_3D = 3 }; /// Indices base class for Fields -- Regions are dereferenced into these /// @@ -170,8 +171,8 @@ struct SpecificInd { int ny = -1, nz = -1; ///< Sizes of y and z dimensions SpecificInd() = default; - SpecificInd(int i, int ny, int nz) : ind(i), ny(ny), nz(nz){}; - explicit SpecificInd(int i) : ind(i){}; + SpecificInd(int i, int ny, int nz) : ind(i), ny(ny), nz(nz) {}; + explicit SpecificInd(int i) : ind(i) {}; /// Allow explicit conversion to an int explicit operator int() const { return ind; } @@ -386,6 +387,7 @@ inline SpecificInd operator-(SpecificInd lhs, const SpecificInd& rhs) { using Ind3D = SpecificInd; using Ind2D = SpecificInd; using IndPerp = SpecificInd; +using IndG3D = SpecificInd; /// Get string representation of Ind3D inline std::string toString(const Ind3D& i) { @@ -490,10 +492,9 @@ template class Region { // Following prevents a Region being created with anything other // than Ind2D, Ind3D or IndPerp as template type - static_assert( - std::is_base_of_v< - Ind2D, T> || std::is_base_of_v || std::is_base_of_v, - "Region must be templated with one of IndPerp, Ind2D or Ind3D"); + static_assert(std::is_base_of_v || std::is_base_of_v + || std::is_base_of_v, + "Region must be templated with one of IndPerp, Ind2D or Ind3D"); public: using data_type = T; @@ -569,7 +570,7 @@ public: }; Region(RegionIndices& indices, int maxregionblocksize = MAXREGIONBLOCKSIZE) - : indices(indices), blocks(getContiguousBlocks(maxregionblocksize)){}; + : indices(indices), blocks(getContiguousBlocks(maxregionblocksize)) {}; // We need to first set the blocks, and only after that call getRegionIndices. // Do not put in the member initialisation @@ -594,17 +595,28 @@ public: const ContiguousBlocks& getBlocks() const { return blocks; }; const RegionIndices& getIndices() const { return indices; }; + const Array& getLinearIndices() const { + if (linearIndices.empty()) { + linearIndices = Array(indices.size()); + for (size_type i = 0; i < indices.size(); ++i) { + linearIndices[i] = indices[i].ind; + } + } + return linearIndices; + } /// Set the indices and ensure blocks updated void setIndices(RegionIndices& indicesIn, int maxregionblocksize = MAXREGIONBLOCKSIZE) { indices = indicesIn; blocks = getContiguousBlocks(maxregionblocksize); + invalidateLinearIndices(); }; /// Set the blocks and ensure indices updated void setBlocks(ContiguousBlocks& blocksIn) { blocks = blocksIn; indices = getRegionIndices(); + invalidateLinearIndices(); }; /// Return a new Region that has the same indices as this one but @@ -828,10 +840,13 @@ public: // sorted this would prevent this usage. private: - RegionIndices indices; //< Flattened indices - ContiguousBlocks blocks; //< Contiguous sections of flattened indices - int ny = -1; //< Size of y dimension - int nz = -1; //< Size of z dimension + RegionIndices indices; //< Flattened indices + ContiguousBlocks blocks; //< Contiguous sections of flattened indices + int ny = -1; //< Size of y dimension + int nz = -1; //< Size of z dimension + mutable Array linearIndices; //< Cached flattened integer indices + + void invalidateLinearIndices() const { linearIndices.clear(); } /// Helper function to create a RegionIndices, given the start and end /// points in x, y, z, and the total y, z lengths @@ -972,6 +987,12 @@ Region operator+(const Region& lhs, const Region& rhs) { return Region(indices); } +template +Region operator+(Region&& lhs, const Region& rhs) { + lhs += rhs; + return std::move(lhs); +} + /// Returns a new region based on input but with indices offset by /// a constant template diff --git a/include/bout/scorepwrapper.hxx b/include/bout/scorepwrapper.hxx index 81761530f0..8b37b2ff61 100644 --- a/include/bout/scorepwrapper.hxx +++ b/include/bout/scorepwrapper.hxx @@ -3,7 +3,6 @@ #include "bout/build_defines.hxx" -#include "bout/msg_stack.hxx" #include #if BOUT_HAS_SCOREP @@ -14,13 +13,25 @@ #define SCOREPLVL 0 #endif +/// The __PRETTY_FUNCTION__ variable is defined by GCC (and some other families) but is +/// not a part of the standard. The __func__ variable *is* a part of the c++11 standard so +/// we'd like to fall back to this if possible. However as these are variables/constants +/// and not macros we can't just check if __PRETTY_FUNCITON__ is defined or not. Instead +/// we need to say if we support this or not by defining BOUT_HAS_PRETTY_FUNCTION (to be +/// implemented in configure) +#if BOUT_HAS_PRETTY_FUNCTION +#define _thefunc_ __PRETTY_FUNCTION__ +#else +#define _thefunc_ __func__ +#endif + /// Instrument a function with scorep /// /// The scorep call is identical for all levels, so just define it here. /// If we don't have scorep support then just define a null function #if BOUT_HAS_SCOREP #define SCOREP_BASE_CALL(...) \ - SCOREP_USER_REGION(__thefunc__, SCOREP_USER_REGION_TYPE_FUNCTION) + SCOREP_USER_REGION(_thefunc_, SCOREP_USER_REGION_TYPE_FUNCTION) #else #define SCOREP_BASE_CALL(...) #endif diff --git a/include/bout/single_index_ops.hxx b/include/bout/single_index_ops.hxx index 60bd78bc36..c29d1a471f 100644 --- a/include/bout/single_index_ops.hxx +++ b/include/bout/single_index_ops.hxx @@ -7,17 +7,6 @@ #include "field_accessor.hxx" -#if BOUT_HAS_RAJA -//-- RAJA CUDA settings--------------------------------------------------------start -#if BOUT_HAS_CUDA -const int CUDA_BLOCK_SIZE = 256; // TODO: Make configurable -using EXEC_POL = RAJA::cuda_exec; -#else // not BOUT_USE_CUDA -using EXEC_POL = RAJA::loop_exec; -#endif // end BOUT_USE_CUDA -////-----------CUDA settings------------------------------------------------------end -#endif // end BOUT_HAS_RAJA - // Ind3D: i.zp(): BOUT_HOST_DEVICE inline int i_zp(const int id, const int nz) { int jz = id % nz; diff --git a/include/bout/solver.hxx b/include/bout/solver.hxx index 446acefecb..09ede6a32b 100644 --- a/include/bout/solver.hxx +++ b/include/bout/solver.hxx @@ -40,16 +40,22 @@ #include "bout/bout_types.hxx" #include "bout/boutexception.hxx" +#include "bout/globals.hxx" +#include "bout/mesh.hxx" #include "bout/monitor.hxx" #include "bout/options.hxx" +#include "bout/region.hxx" #include "bout/unused.hxx" +#include +#include #include /////////////////////////////////////////////////////////////////// // C function pointer types class Solver; +class SundialsNVectorInterface; /// RHS function pointer using rhsfunc = int (*)(BoutReal); @@ -91,7 +97,8 @@ constexpr auto SOLVERIMEXBDF2 = "imexbdf2"; constexpr auto SOLVERSNES = "snes"; constexpr auto SOLVERRKGENERIC = "rkgeneric"; -enum class SOLVER_VAR_OP { LOAD_VARS, LOAD_DERIVS, SET_ID, SAVE_VARS, SAVE_DERIVS }; +enum class FieldCategories : std::uint8_t { VARS, DERIVS, MMS }; +enum class SOLVER_VAR_OP : std::uint8_t { LOAD, SET_ID, SAVE }; /// A type to set where in the list monitors are added enum class MonitorPosition { BACK, FRONT }; @@ -358,6 +365,8 @@ public: int getIterationOffset() const { return iteration_offset; } protected: + friend class SundialsNVectorInterface; + /// Number of command-line arguments static int* pargc; /// Command-line arguments @@ -388,6 +397,114 @@ protected: std::string description{""}; /// Description of what the variable is }; + /// A structure for iterating over fields + template + struct VarIterator { + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using pointer = T*; + using reference = T&; + using underlying_iterator = std::vector>::iterator; + using difference_type = std::iterator_traits::difference_type; + + VarIterator(underlying_iterator _it) : it(std::move(_it)) {} + + reference operator*() const { + if constexpr (C == FieldCategories::VARS) { + return *(it->var); + } else if constexpr (C == FieldCategories::DERIVS) { + return *(it->F_var); + } else { + static_assert(C == FieldCategories::MMS); + return *(it->MMS_err); + } + } + pointer operator->() const { + if constexpr (C == FieldCategories::VARS) { + return it->var; + } else if constexpr (C == FieldCategories::DERIVS) { + return it->F_var; + } else { + static_assert(C == FieldCategories::MMS); + return it->MMS_err.get(); + } + } + + VarIterator& operator++() { + it++; + return *this; + } + VarIterator operator++(int) { + VarIterator tmp = *this; + ++(*this); + return tmp; + } + VarIterator& operator+=(difference_type n) { + it += n; + return *this; + } + VarIterator operator+(difference_type n) const { return VarIterator(it + n); } + friend VarIterator operator+(difference_type n, const VarIterator& a) { + return VarIterator(n + a.it); + } + + VarIterator& operator--() { + it--; + return *this; + } + VarIterator operator--(int) { + VarIterator tmp = *this; + --(*this); + return tmp; + } + VarIterator& operator-=(difference_type n) { + it -= n; + return *this; + } + VarIterator operator-(difference_type n) const { return VarIterator(it - n); } + friend VarIterator operator-(difference_type n, const VarIterator& a) { + return VarIterator(n - a.it); + } + + difference_type operator-(const VarIterator& b) { return it - b.it; } + reference operator[](difference_type n) { return *VarIterator(it[n]); } + + friend bool operator==(const VarIterator& a, const VarIterator& b) { + return a.it == b.it; + } + friend bool operator!=(const VarIterator& a, const VarIterator& b) { + return a.it != b.it; + } + friend bool operator<(const VarIterator& a, const VarIterator& b) { + return a.it < b.it; + } + friend bool operator>(const VarIterator& a, const VarIterator& b) { + return a.it > b.it; + } + friend bool operator<=(const VarIterator& a, const VarIterator& b) { + return a.it <= b.it; + } + friend bool operator>=(const VarIterator& a, const VarIterator& b) { + return a.it >= b.it; + } + + private: + underlying_iterator it{}; + }; + + template + struct VarRange { + using iterator = VarIterator; + + VarRange(std::vector>& vars) : _begin(vars.begin()), _end(vars.end()) {} + + iterator begin() const { return _begin; } + iterator end() const { return _end; } + + private: + iterator _begin, _end; + }; + /// Does \p var represent field \p name? template friend bool operator==(const VarStr& var, const std::string& name) { @@ -451,19 +568,19 @@ protected: /// /// There are two important things to note about how \p iter is /// passed along to each monitor: - /// - The solvers all start their iteration numbering from zero, so the - /// initial state is calculated at \p iter = -1 + /// - The initial state is written at \p iter = 0, and solver output + /// steps are numbered from 1 to NOUT /// - Secondly, \p iter is passed along to each monitor *relative to /// that monitor's period* /// /// In practice, this means that each monitor is called like: /// /// monitor->call(solver, simulation_time, - /// ((iter + 1) / monitor->period) - 1, + /// iter / monitor->period, /// NOUT / monitor->period); /// - /// e.g. for a monitor with period 10, passing \p iter = 9 will - /// result in it being called with a value of `(9 + 1)/10 - 1 == 0` + /// e.g. for a monitor with period 10, passing \p iter = 10 will + /// result in it being called with a value of `10/10 == 1` int call_monitors(BoutReal simtime, int iter, int NOUT); /// Should timesteps be monitored? @@ -516,6 +633,28 @@ protected: /// Get the currently set output timestep BoutReal getOutputTimestep() const { return output_timestep; } + /// Loop over variables (accessed by iterating the f2d_range and f3d_range + /// arguments) and domain. Used for all data operations for + /// consistency + template + void loop_vars(RangeF2D f2d_range, RangeF3D f3d_range, BoutReal* udata, + SOLVER_VAR_OP op) { + // Use global mesh: FIX THIS! + const Mesh* mesh = bout::globals::mesh; + + int p = 0; // Counter for location in udata array + + // All boundaries + for (const auto& i2d : mesh->getRegion2D("RGN_BNDRY")) { + loop_vars_op(f2d_range, f3d_range, i2d, udata, p, op, true); + } + + // Bulk of points + for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { + loop_vars_op(f2d_range, f3d_range, i2d, udata, p, op, false); + } + } + private: /// Generate a random UUID (version 4) and broadcast it to all processors std::string createRunID() const; @@ -570,9 +709,124 @@ private: /// Should be run after user RHS is called void post_rhs(BoutReal t); - /// Loading data from BOUT++ to/from solver - void loop_vars_op(Ind2D i2d, BoutReal* udata, int& p, SOLVER_VAR_OP op, bool bndry); - void loop_vars(BoutReal* udata, SOLVER_VAR_OP op); + /// Perform an operation at a given Ind2D (jx,jy) location, moving + /// data between BOUT++ and solver. This can be done on arbitrary + /// sets of fields (accessed using f2d_range and f3d_range), provided they + /// are the same number, shape, size, etc. as the fields being + /// solved for. + template + void loop_vars_op(RangeF2D f2d_range, RangeF3D f3d_range, Ind2D i2d, BoutReal* udata, + int& p, SOLVER_VAR_OP op, bool bndry) { + /************************************************************************** + * Looping over variables + * + * NOTE: This part is very inefficient, and should be replaced ASAP + * Is the interleaving of variables needed or helpful to the solver? + **************************************************************************/ + // Use global mesh: FIX THIS! + const Mesh* mesh = bout::globals::mesh; + + const int nz = mesh->LocalNz; + + switch (op) { + case SOLVER_VAR_OP::LOAD: { + /// Load variables from IDA into BOUT++ + + // Loop over 2D variables + auto metadata_it = f2d.begin(); + for (auto& field : f2d_range) { + const auto& metadata = *metadata_it; + ++metadata_it; + if (bndry && !metadata.evolve_bndry) { + continue; + } + field[i2d] = udata[p]; + p++; + } + + for (int jz = 0; jz < nz; jz++) { + + auto metadata_it = f3d.begin(); + // Loop over 3D variables + for (auto& field : f3d_range) { + const auto& metadata = *metadata_it; + ++metadata_it; + if (bndry && !metadata.evolve_bndry) { + continue; + } + field[field.getMesh()->ind2Dto3D(i2d, jz)] = udata[p]; + p++; + } + } + break; + } + case SOLVER_VAR_OP::SET_ID: { + /// Set the type of equation (Differential or Algebraic) + + // Loop over 2D variables + for (const auto& metadata : f2d) { + if (bndry && !metadata.evolve_bndry) { + continue; + } + if (metadata.constraint) { + udata[p] = 0; + } else { + udata[p] = 1; + } + p++; + } + + for (int jz = 0; jz < nz; jz++) { + + // Loop over 3D variables + for (const auto& metadata : f3d) { + if (bndry && !metadata.evolve_bndry) { + continue; + } + if (metadata.constraint) { + udata[p] = 0; + } else { + udata[p] = 1; + } + p++; + } + } + + break; + } + case SOLVER_VAR_OP::SAVE: { + /// Save variables from BOUT++ into IDA (only used at start of simulation) + + // Loop over 2D variables + auto metadata_it = f2d.begin(); + for (const auto& field : f2d_range) { + const auto& metadata = *metadata_it; + ++metadata_it; + if (bndry && !metadata.evolve_bndry) { + continue; + } + udata[p] = field[i2d]; + p++; + } + + for (int jz = 0; jz < nz; jz++) { + + auto metadata_it = f3d.begin(); + // Loop over 3D variables + for (const auto& field : f3d_range) { + const auto& metadata = *metadata_it; + ++metadata_it; + if (bndry && !metadata.evolve_bndry) { + continue; + } + udata[p] = field[field.getMesh()->ind2Dto3D(i2d, jz)]; + p++; + } + } + break; + } + } + } /// Check if a variable has already been added bool varAdded(const std::string& name); diff --git a/include/bout/sundials_backports.hxx b/include/bout/sundials_backports.hxx index 1ab39595ec..bb17e593b2 100644 --- a/include/bout/sundials_backports.hxx +++ b/include/bout/sundials_backports.hxx @@ -32,10 +32,11 @@ // NOLINTBEGIN(cppcoreguidelines-macro-usage) #define SUNDIALS_VERSION_AT_LEAST(major, minor, patch) \ - ((major) < SUNDIALS_VERSION_MAJOR \ - || ((major) == SUNDIALS_VERSION_MAJOR \ - && ((minor) < SUNDIALS_VERSION_MINOR \ - || ((minor) == SUNDIALS_VERSION_MINOR && (patch) <= SUNDIALS_VERSION_PATCH)))) + ((major) < SUNDIALS_VERSION_MAJOR \ + || ((major) == SUNDIALS_VERSION_MAJOR \ + && ((minor) < SUNDIALS_VERSION_MINOR \ + || ((minor) == SUNDIALS_VERSION_MINOR \ + && (patch) <= SUNDIALS_VERSION_PATCH)))) #define SUNDIALS_VERSION_LESS_THAN(major, minor, patch) \ (!SUNDIALS_VERSION_AT_LEAST(major, minor, patch)) // NOLINTEND(cppcoreguidelines-macro-usage) @@ -77,13 +78,13 @@ inline sundials::Context createSUNContext([[maybe_unused]] MPI_Comm& comm) { #endif } -template -inline decltype(auto) callWithSUNContext(Func f, [[maybe_unused]] sundials::Context& ctx, +template +inline decltype(auto) callWithSUNContext(Func f, [[maybe_unused]] Ctx&& ctx, Args&&... args) { #if SUNDIALS_VERSION_LESS_THAN(6, 0, 0) return f(std::forward(args)...); #else - return f(std::forward(args)..., ctx); + return f(std::forward(args)..., std::forward(ctx)); #endif } diff --git a/include/bout/sys/expressionparser.hxx b/include/bout/sys/expressionparser.hxx index 660ad20ab3..8b2442e843 100644 --- a/include/bout/sys/expressionparser.hxx +++ b/include/bout/sys/expressionparser.hxx @@ -29,7 +29,8 @@ #include "bout/unused.hxx" -#include "fmt/core.h" +#include +#include #include #include @@ -64,12 +65,6 @@ public: return nullptr; } - [[deprecated("This will be removed in a future version. Implementations should " - "override the Context version of this function.")]] virtual double - generate(BoutReal x, BoutReal y, BoutReal z, BoutReal t) { - return generate(bout::generator::Context().set("x", x, "y", y, "z", z, "t", t)); - } - /// Generate a value at the given coordinates (x,y,z,t) /// This should be deterministic, always returning the same value given the same inputs /// @@ -78,7 +73,7 @@ public: /// them or an infinite recursion results. This is for backward /// compatibility for users and implementors. In a future version /// this function will be made pure virtual. - virtual double generate(const bout::generator::Context& ctx); + virtual double generate(const bout::generator::Context& ctx) = 0; /// Create a string representation of the generator, for debugging output virtual std::string str() const { return std::string("?"); } @@ -245,11 +240,16 @@ private: class ParseException : public std::exception { public: + ParseException(const ParseException&) = default; + ParseException(ParseException&&) = delete; + ParseException& operator=(const ParseException&) = default; + ParseException& operator=(ParseException&&) = delete; ParseException(const std::string& message_) : message(message_) {} - template - ParseException(const S& format, const Args&... args) - : message(fmt::format(format, args...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + ParseException(fmt::format_string format, Args&&... args) + : message(fmt::vformat(format, fmt::make_format_args(args...))) {} ~ParseException() override = default; diff --git a/include/bout/sys/gettext.hxx b/include/bout/sys/gettext.hxx index a03d7563fd..751f51882e 100644 --- a/include/bout/sys/gettext.hxx +++ b/include/bout/sys/gettext.hxx @@ -8,15 +8,47 @@ #if BOUT_HAS_GETTEXT -#include +#include // IWYU pragma: keep + #include #define GETTEXT_PACKAGE "libbout" -#define _(string) dgettext(GETTEXT_PACKAGE, string) +// If we have C++23, we can get fmt to do compile-time checks of our format +// strings, _and_ have gettext do runtime replacement +#if __cpp_if_consteval >= 202106L +constexpr const char* dgettext_wrap(const char* __domainname, const char* __msgid) __THROW + __attribute_format_arg__(2); + +constexpr const char* dgettext_wrap(const char* __domainname, const char* __msgid) { + if consteval { + return __msgid; + } + return dgettext(__domainname, __msgid); +} + +/// Gettext i18n macro for text containing fmt format specifiers +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _f(string) dgettext_wrap(GETTEXT_PACKAGE, string) #else +// We're pre-C++23, so all our i18n text must be fmt runtime formats +#include "fmt/base.h" // IWYU pragma: keep + +/// Gettext i18n macro for text containing fmt format specifiers +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _f(string) fmt::runtime(dgettext(GETTEXT_PACKAGE, string)) +#endif + +/// Gettext i18n macro for plain text that doesn't need formatting +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _(string) dgettext(GETTEXT_PACKAGE, string) + +#else // BOUT_HAS_GETTEXT +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _f(string) string +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _(string) string #endif // BOUT_HAS_GETTEXT diff --git a/include/bout/sys/parallel_stencils.hxx b/include/bout/sys/parallel_stencils.hxx new file mode 100644 index 0000000000..88396005ae --- /dev/null +++ b/include/bout/sys/parallel_stencils.hxx @@ -0,0 +1,34 @@ +#pragma once +#include +#include + +namespace bout::parallel_stencil { +// generated by src/mesh/parallel_boundary_stencil.cxx.py +using std::pow; + +inline BoutReal dirichlet_o1([[maybe_unused]] BoutReal spacing0, BoutReal value0) { + return value0; +} +inline BoutReal dirichlet_o2(BoutReal spacing0, BoutReal value0, BoutReal spacing1, + BoutReal value1) { + return (spacing0 * value1 - spacing1 * value0) / (spacing0 - spacing1); +} +inline BoutReal neumann_o2([[maybe_unused]] BoutReal spacing0, BoutReal value0, + BoutReal spacing1, BoutReal value1) { + return (-spacing1 * value0) + value1; +} +inline BoutReal dirichlet_o3(BoutReal spacing0, BoutReal value0, BoutReal spacing1, + BoutReal value1, BoutReal spacing2, BoutReal value2) { + return (pow(spacing0, 2) * spacing1 * value2 - pow(spacing0, 2) * spacing2 * value1 + - spacing0 * pow(spacing1, 2) * value2 + spacing0 * pow(spacing2, 2) * value1 + + pow(spacing1, 2) * spacing2 * value0 - spacing1 * pow(spacing2, 2) * value0) + / ((spacing0 - spacing1) * (spacing0 - spacing2) * (spacing1 - spacing2)); +} +inline BoutReal neumann_o3(BoutReal spacing0, BoutReal value0, BoutReal spacing1, + BoutReal value1, BoutReal spacing2, BoutReal value2) { + return (2 * spacing0 * spacing1 * value2 - 2 * spacing0 * spacing2 * value1 + + pow(spacing1, 2) * spacing2 * value0 - pow(spacing1, 2) * value2 + - spacing1 * pow(spacing2, 2) * value0 + pow(spacing2, 2) * value1) + / ((spacing1 - spacing2) * (2 * spacing0 - spacing1 - spacing2)); +} +} // namespace bout::parallel_stencil diff --git a/include/bout/sys/timer.hxx b/include/bout/sys/timer.hxx index f3beba27b1..c6a0c9020a 100644 --- a/include/bout/sys/timer.hxx +++ b/include/bout/sys/timer.hxx @@ -7,7 +7,6 @@ #include #include "bout/msg_stack.hxx" -#include "bout/output.hxx" /*! * Timing class for performance benchmarking and diagnosis @@ -133,5 +132,4 @@ public: static void printTimeReport(); }; -#define AUTO_TIME() Timer CONCATENATE(time_, __LINE__)(__thefunc__) #endif // BOUT_TIMER_H diff --git a/include/bout/sys/type_name.hxx b/include/bout/sys/type_name.hxx index be4d096b4d..fcd251bf7a 100644 --- a/include/bout/sys/type_name.hxx +++ b/include/bout/sys/type_name.hxx @@ -4,13 +4,19 @@ #ifndef TYPE_NAME_HXX #define TYPE_NAME_HXX +#include "bout/array.hxx" #include "bout/bout_types.hxx" + #include #include class Field2D; class Field3D; class FieldPerp; +template +class Matrix; +template +class Tensor; namespace bout { namespace utils { @@ -41,6 +47,19 @@ std::string typeName(); template <> std::string typeName(); + +template <> +std::string typeName>(); +template <> +std::string typeName>(); +template <> +std::string typeName>(); +template <> +std::string typeName>(); +template <> +std::string typeName>(); +template <> +std::string typeName>(); } // namespace utils } // namespace bout diff --git a/include/bout/sys/uuid.h b/include/bout/sys/uuid.h index 50accb506c..ecc8c0d4e3 100644 --- a/include/bout/sys/uuid.h +++ b/include/bout/sys/uuid.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,7 @@ namespace uuids { namespace detail { template -constexpr inline unsigned char hex2char(TChar const ch) { +constexpr unsigned char hex2char(const TChar ch) { if (ch >= static_cast('0') && ch <= static_cast('9')) return ch - static_cast('0'); if (ch >= static_cast('a') && ch <= static_cast('f')) @@ -87,14 +88,14 @@ constexpr inline unsigned char hex2char(TChar const ch) { } template -constexpr inline bool is_hex(TChar const ch) { +constexpr bool is_hex(const TChar ch) { return (ch >= static_cast('0') && ch <= static_cast('9')) || (ch >= static_cast('a') && ch <= static_cast('f')) || (ch >= static_cast('A') && ch <= static_cast('F')); } template -constexpr inline unsigned char hexpair2char(TChar const a, TChar const b) { +constexpr unsigned char hexpair2char(const TChar a, const TChar b) { return (hex2char(a) << 4) | hex2char(b); } @@ -105,7 +106,7 @@ class sha1 { static constexpr unsigned int block_bytes = 64; - inline static uint32_t left_rotate(uint32_t value, size_t const count) { + static uint32_t left_rotate(uint32_t value, const size_t count) { return (value << count) ^ (value >> (32 - count)); } @@ -130,7 +131,7 @@ class sha1 { } } - void process_block(void const* const start, void const* const end) { + void process_block(const void* const start, const void* const end) { const uint8_t* begin = static_cast(start); const uint8_t* finish = static_cast(end); while (begin != finish) { @@ -139,13 +140,13 @@ class sha1 { } } - void process_bytes(void const* const data, size_t const len) { + void process_bytes(const void* const data, const size_t len) { const uint8_t* block = static_cast(data); process_block(block, block + len); } - uint32_t const* get_digest(digest32_t digest) { - size_t const bitCount = this->m_byteCount * 8; + const uint32_t* get_digest(digest32_t digest) { + const size_t bitCount = this->m_byteCount * 8; process_byte(0x80); if (this->m_blockByteIndex > 56) { while (m_blockByteIndex != 0) { @@ -166,13 +167,13 @@ class sha1 { process_byte(static_cast((bitCount >> 24) & 0xFF)); process_byte(static_cast((bitCount >> 16) & 0xFF)); process_byte(static_cast((bitCount >> 8) & 0xFF)); - process_byte(static_cast((bitCount)&0xFF)); + process_byte(static_cast((bitCount) & 0xFF)); memcpy(digest, m_digest, 5 * sizeof(uint32_t)); return digest; } - uint8_t const* get_digest_bytes(digest8_t digest) { + const uint8_t* get_digest_bytes(digest8_t digest) { digest32_t d32; get_digest(d32); size_t di = 0; @@ -344,13 +345,13 @@ class uuid { public: using value_type = uint8_t; - constexpr uuid() noexcept : data({}){}; + constexpr uuid() noexcept : data({}) {}; uuid(value_type (&arr)[16]) noexcept { std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); } - uuid(std::array const& arr) noexcept { + uuid(const std::array& arr) noexcept { std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); } @@ -395,12 +396,10 @@ class uuid { void swap(uuid& other) noexcept { data.swap(other.data); } - inline char const* as_bytes() const { - return reinterpret_cast(data.data()); - } + const char* as_bytes() const { return reinterpret_cast(data.data()); } template - static bool is_valid_uuid(CharT const* str) noexcept { + static bool is_valid_uuid(const CharT* str) noexcept { bool firstDigit = true; int hasBraces = 0; size_t index = 0; @@ -455,12 +454,12 @@ class uuid { template , class Allocator = std::allocator> static bool - is_valid_uuid(std::basic_string const& str) noexcept { + is_valid_uuid(const std::basic_string& str) noexcept { return is_valid_uuid(str.c_str()); } template - static uuid from_string(CharT const* str) noexcept { + static uuid from_string(const CharT* str) noexcept { CharT digit = 0; bool firstDigit = true; int hasBraces = 0; @@ -512,40 +511,40 @@ class uuid { template , class Allocator = std::allocator> static uuid - from_string(std::basic_string const& str) noexcept { + from_string(const std::basic_string& str) noexcept { return from_string(str.c_str()); } private: std::array data{{0}}; - friend bool operator==(uuid const& lhs, uuid const& rhs) noexcept; - friend bool operator<(uuid const& lhs, uuid const& rhs) noexcept; + friend bool operator==(const uuid& lhs, const uuid& rhs) noexcept; + friend bool operator<(const uuid& lhs, const uuid& rhs) noexcept; template friend std::basic_ostream& operator<<(std::basic_ostream& s, - uuid const& id); + const uuid& id); }; // -------------------------------------------------------------------------------------------------------------------------- // operators and non-member functions // -------------------------------------------------------------------------------------------------------------------------- -inline bool operator==(uuid const& lhs, uuid const& rhs) noexcept { +inline bool operator==(const uuid& lhs, const uuid& rhs) noexcept { return lhs.data == rhs.data; } -inline bool operator!=(uuid const& lhs, uuid const& rhs) noexcept { +inline bool operator!=(const uuid& lhs, const uuid& rhs) noexcept { return !(lhs == rhs); } -inline bool operator<(uuid const& lhs, uuid const& rhs) noexcept { +inline bool operator<(const uuid& lhs, const uuid& rhs) noexcept { return lhs.data < rhs.data; } template std::basic_ostream& operator<<(std::basic_ostream& s, - uuid const& id) { + const uuid& id) { // save current flags std::ios_base::fmtflags f(s.flags()); @@ -568,7 +567,7 @@ std::basic_ostream& operator<<(std::basic_ostream& s template , class Allocator = std::allocator> -inline std::basic_string to_string(uuid const& id) { +inline std::basic_string to_string(const uuid& id) { std::basic_stringstream sstr; sstr << id; return sstr.str(); @@ -692,11 +691,11 @@ using uuid_random_generator = basic_uuid_random_generator; class uuid_name_generator { public: - explicit uuid_name_generator(uuid const& namespace_uuid) noexcept + explicit uuid_name_generator(const uuid& namespace_uuid) noexcept : nsuuid(namespace_uuid) {} template - uuid operator()(CharT const* name) { + uuid operator()(const CharT* name) { size_t size = 0; if (std::is_same::value) size = strlen(name); @@ -710,7 +709,7 @@ class uuid_name_generator { template , class Allocator = std::allocator> - uuid operator()(std::basic_string const& name) { + uuid operator()(const std::basic_string& name) { reset(); process_characters(name.data(), name.size()); return make_uuid(); @@ -727,7 +726,7 @@ class uuid_name_generator { template ::value>> - void process_characters(char_type const* const characters, size_t const count) { + void process_characters(const char_type* const characters, const size_t count) { for (size_t i = 0; i < count; i++) { uint32_t c = characters[i]; hasher.process_byte(static_cast((c >> 0) & 0xFF)); @@ -737,7 +736,7 @@ class uuid_name_generator { } } - void process_characters(const char* const characters, size_t const count) { + void process_characters(const char* const characters, const size_t count) { hasher.process_bytes(characters, count); } @@ -847,7 +846,7 @@ struct hash { using argument_type = uuids::uuid; using result_type = std::size_t; - result_type operator()(argument_type const& uuid) const { + result_type operator()(const argument_type& uuid) const { std::hash hasher; return static_cast(hasher(uuids::to_string(uuid))); } diff --git a/include/bout/tokamak_coordinates.hxx b/include/bout/tokamak_coordinates.hxx new file mode 100644 index 0000000000..f056033bb9 --- /dev/null +++ b/include/bout/tokamak_coordinates.hxx @@ -0,0 +1,49 @@ +#ifndef BOUT_TOKAMAK_COORDINATES_HXX +#define BOUT_TOKAMAK_COORDINATES_HXX + +#include +#include +#include + +class Mesh; + +namespace bout { +/// Variables used in the BOUT++ tokamak coordinate system. +/// +/// When created by `set_tokamak_coordinates`, all variables except +/// `I_unnormalised` are normalised. +struct TokamakCoordinates { + /// Major radius + Field2D Rxy; + /// Vertical height + Field2D Zxy; + /// Poloidal magnetic field + Field2D Bpxy; + /// Toroidal magnetic field + Field2D Btxy; + /// Total magnetic field + Field2D Bxy; + /// Poloidal arc length + Field2D hthe; + /// Integrated shear (normalised) + Coordinates::FieldMetric I; + /// Unnormalised integrated shear + Coordinates::FieldMetric I_unnormalised; +}; + +/// Read, normalise, calculate, and set the metric components for a BOUT++ +/// tokamak coordinate system. +/// +/// Sets the cell-centre `Coordinates` on \p mesh +/// +/// @param Lbar Spatial normalisation +/// @param Bbar Magnetic normalisation +/// @param no_shear Don't use integrated shear in coordinate system +/// @param shear_factor Integrated shear normalisation +TokamakCoordinates set_tokamak_coordinates(Mesh& mesh, BoutReal Lbar = 1.0, + BoutReal Bbar = 1.0, bool no_shear = false, + BoutReal shear_factor = 1.0); + +} // namespace bout + +#endif //BOUT_TOKAMAK_COORDINATES_HXX diff --git a/include/bout/traits.hxx b/include/bout/traits.hxx index 4e03d6b526..5796c65879 100644 --- a/include/bout/traits.hxx +++ b/include/bout/traits.hxx @@ -1,6 +1,7 @@ #ifndef BOUT_TRAITS_H #define BOUT_TRAITS_H +#include #include class Field; @@ -112,6 +113,10 @@ using EnableIfFieldPerp = /// Enable a function if T is a subclass of Options template using EnableIfOptions = std::enable_if_t>; + +/// Create a `std::index_sequence` with the length of a tuple ``T`` +template +using tuple_index_sequence = std::make_index_sequence>; } // namespace utils } // namespace bout diff --git a/include/bout/twiddle.hxx b/include/bout/twiddle.hxx new file mode 100644 index 0000000000..6da72dd8ff --- /dev/null +++ b/include/bout/twiddle.hxx @@ -0,0 +1,1025 @@ +__constant__ double2 c_twiddle_16[16] = { + {1.0000000000000000, -0.0000000000000000}, // k=0 + {0.9238795325112867, -0.3826834323650898}, // k=1 + {0.7071067811865476, -0.7071067811865475}, // k=2 + {0.3826834323650898, -0.9238795325112867}, // k=3 + {0.0000000000000001, -1.0000000000000000}, // k=4 + {-0.3826834323650897, -0.9238795325112867}, // k=5 + {-0.7071067811865475, -0.7071067811865476}, // k=6 + {-0.9238795325112867, -0.3826834323650899}, // k=7 + {-1.0000000000000000, -0.0000000000000001}, // k=8 + {-0.9238795325112868, 0.3826834323650897}, // k=9 + {-0.7071067811865477, 0.7071067811865475}, // k=10 + {-0.3826834323650903, 0.9238795325112865}, // k=11 + {-0.0000000000000002, 1.0000000000000000}, // k=12 + {0.3826834323650900, 0.9238795325112866}, // k=13 + {0.7071067811865474, 0.7071067811865477}, // k=14 + {0.9238795325112865, 0.3826834323650904}, // k=15 +}; + +__constant__ double2 c_twiddle_32[32] = { + {1.0000000000000000, -0.0000000000000000}, // k=0 + {0.9807852804032304, -0.1950903220161282}, // k=1 + {0.9238795325112867, -0.3826834323650898}, // k=2 + {0.8314696123025452, -0.5555702330196022}, // k=3 + {0.7071067811865476, -0.7071067811865475}, // k=4 + {0.5555702330196023, -0.8314696123025452}, // k=5 + {0.3826834323650898, -0.9238795325112867}, // k=6 + {0.1950903220161283, -0.9807852804032304}, // k=7 + {0.0000000000000001, -1.0000000000000000}, // k=8 + {-0.1950903220161282, -0.9807852804032304}, // k=9 + {-0.3826834323650897, -0.9238795325112867}, // k=10 + {-0.5555702330196020, -0.8314696123025455}, // k=11 + {-0.7071067811865475, -0.7071067811865476}, // k=12 + {-0.8314696123025453, -0.5555702330196022}, // k=13 + {-0.9238795325112867, -0.3826834323650899}, // k=14 + {-0.9807852804032304, -0.1950903220161286}, // k=15 + {-1.0000000000000000, -0.0000000000000001}, // k=16 + {-0.9807852804032304, 0.1950903220161284}, // k=17 + {-0.9238795325112868, 0.3826834323650897}, // k=18 + {-0.8314696123025455, 0.5555702330196020}, // k=19 + {-0.7071067811865477, 0.7071067811865475}, // k=20 + {-0.5555702330196022, 0.8314696123025452}, // k=21 + {-0.3826834323650903, 0.9238795325112865}, // k=22 + {-0.1950903220161287, 0.9807852804032303}, // k=23 + {-0.0000000000000002, 1.0000000000000000}, // k=24 + {0.1950903220161283, 0.9807852804032304}, // k=25 + {0.3826834323650900, 0.9238795325112866}, // k=26 + {0.5555702330196018, 0.8314696123025455}, // k=27 + {0.7071067811865474, 0.7071067811865477}, // k=28 + {0.8314696123025452, 0.5555702330196022}, // k=29 + {0.9238795325112865, 0.3826834323650904}, // k=30 + {0.9807852804032303, 0.1950903220161287}, // k=31 +}; + +__constant__ double2 c_twiddle_64[64] = { + {1.0000000000000000, -0.0000000000000000}, // k=0 + {0.9951847266721969, -0.0980171403295606}, // k=1 + {0.9807852804032304, -0.1950903220161282}, // k=2 + {0.9569403357322088, -0.2902846772544623}, // k=3 + {0.9238795325112867, -0.3826834323650898}, // k=4 + {0.8819212643483550, -0.4713967368259976}, // k=5 + {0.8314696123025452, -0.5555702330196022}, // k=6 + {0.7730104533627370, -0.6343932841636455}, // k=7 + {0.7071067811865476, -0.7071067811865475}, // k=8 + {0.6343932841636455, -0.7730104533627370}, // k=9 + {0.5555702330196023, -0.8314696123025452}, // k=10 + {0.4713967368259978, -0.8819212643483549}, // k=11 + {0.3826834323650898, -0.9238795325112867}, // k=12 + {0.2902846772544623, -0.9569403357322089}, // k=13 + {0.1950903220161283, -0.9807852804032304}, // k=14 + {0.0980171403295608, -0.9951847266721968}, // k=15 + {0.0000000000000001, -1.0000000000000000}, // k=16 + {-0.0980171403295606, -0.9951847266721969}, // k=17 + {-0.1950903220161282, -0.9807852804032304}, // k=18 + {-0.2902846772544622, -0.9569403357322089}, // k=19 + {-0.3826834323650897, -0.9238795325112867}, // k=20 + {-0.4713967368259977, -0.8819212643483550}, // k=21 + {-0.5555702330196020, -0.8314696123025455}, // k=22 + {-0.6343932841636454, -0.7730104533627371}, // k=23 + {-0.7071067811865475, -0.7071067811865476}, // k=24 + {-0.7730104533627370, -0.6343932841636455}, // k=25 + {-0.8314696123025453, -0.5555702330196022}, // k=26 + {-0.8819212643483549, -0.4713967368259979}, // k=27 + {-0.9238795325112867, -0.3826834323650899}, // k=28 + {-0.9569403357322088, -0.2902846772544624}, // k=29 + {-0.9807852804032304, -0.1950903220161286}, // k=30 + {-0.9951847266721968, -0.0980171403295608}, // k=31 + {-1.0000000000000000, -0.0000000000000001}, // k=32 + {-0.9951847266721969, 0.0980171403295606}, // k=33 + {-0.9807852804032304, 0.1950903220161284}, // k=34 + {-0.9569403357322089, 0.2902846772544621}, // k=35 + {-0.9238795325112868, 0.3826834323650897}, // k=36 + {-0.8819212643483550, 0.4713967368259976}, // k=37 + {-0.8314696123025455, 0.5555702330196020}, // k=38 + {-0.7730104533627371, 0.6343932841636453}, // k=39 + {-0.7071067811865477, 0.7071067811865475}, // k=40 + {-0.6343932841636459, 0.7730104533627367}, // k=41 + {-0.5555702330196022, 0.8314696123025452}, // k=42 + {-0.4713967368259979, 0.8819212643483549}, // k=43 + {-0.3826834323650903, 0.9238795325112865}, // k=44 + {-0.2902846772544624, 0.9569403357322088}, // k=45 + {-0.1950903220161287, 0.9807852804032303}, // k=46 + {-0.0980171403295605, 0.9951847266721969}, // k=47 + {-0.0000000000000002, 1.0000000000000000}, // k=48 + {0.0980171403295601, 0.9951847266721969}, // k=49 + {0.1950903220161283, 0.9807852804032304}, // k=50 + {0.2902846772544621, 0.9569403357322089}, // k=51 + {0.3826834323650900, 0.9238795325112866}, // k=52 + {0.4713967368259976, 0.8819212643483550}, // k=53 + {0.5555702330196018, 0.8314696123025455}, // k=54 + {0.6343932841636456, 0.7730104533627369}, // k=55 + {0.7071067811865474, 0.7071067811865477}, // k=56 + {0.7730104533627367, 0.6343932841636459}, // k=57 + {0.8314696123025452, 0.5555702330196022}, // k=58 + {0.8819212643483548, 0.4713967368259979}, // k=59 + {0.9238795325112865, 0.3826834323650904}, // k=60 + {0.9569403357322088, 0.2902846772544625}, // k=61 + {0.9807852804032303, 0.1950903220161287}, // k=62 + {0.9951847266721969, 0.0980171403295605}, // k=63 +}; + +__constant__ double2 c_twiddle_128[128] = { + {1.0000000000000000, -0.0000000000000000}, // k=0 + {0.9987954562051724, -0.0490676743274180}, // k=1 + {0.9951847266721969, -0.0980171403295606}, // k=2 + {0.9891765099647810, -0.1467304744553617}, // k=3 + {0.9807852804032304, -0.1950903220161282}, // k=4 + {0.9700312531945440, -0.2429801799032639}, // k=5 + {0.9569403357322088, -0.2902846772544623}, // k=6 + {0.9415440651830208, -0.3368898533922201}, // k=7 + {0.9238795325112867, -0.3826834323650898}, // k=8 + {0.9039892931234433, -0.4275550934302821}, // k=9 + {0.8819212643483550, -0.4713967368259976}, // k=10 + {0.8577286100002721, -0.5141027441932217}, // k=11 + {0.8314696123025452, -0.5555702330196022}, // k=12 + {0.8032075314806449, -0.5956993044924334}, // k=13 + {0.7730104533627370, -0.6343932841636455}, // k=14 + {0.7409511253549591, -0.6715589548470183}, // k=15 + {0.7071067811865476, -0.7071067811865475}, // k=16 + {0.6715589548470183, -0.7409511253549591}, // k=17 + {0.6343932841636455, -0.7730104533627370}, // k=18 + {0.5956993044924335, -0.8032075314806448}, // k=19 + {0.5555702330196023, -0.8314696123025452}, // k=20 + {0.5141027441932217, -0.8577286100002721}, // k=21 + {0.4713967368259978, -0.8819212643483549}, // k=22 + {0.4275550934302822, -0.9039892931234433}, // k=23 + {0.3826834323650898, -0.9238795325112867}, // k=24 + {0.3368898533922201, -0.9415440651830208}, // k=25 + {0.2902846772544623, -0.9569403357322089}, // k=26 + {0.2429801799032640, -0.9700312531945440}, // k=27 + {0.1950903220161283, -0.9807852804032304}, // k=28 + {0.1467304744553617, -0.9891765099647810}, // k=29 + {0.0980171403295608, -0.9951847266721968}, // k=30 + {0.0490676743274181, -0.9987954562051724}, // k=31 + {0.0000000000000001, -1.0000000000000000}, // k=32 + {-0.0490676743274180, -0.9987954562051724}, // k=33 + {-0.0980171403295606, -0.9951847266721969}, // k=34 + {-0.1467304744553616, -0.9891765099647810}, // k=35 + {-0.1950903220161282, -0.9807852804032304}, // k=36 + {-0.2429801799032639, -0.9700312531945440}, // k=37 + {-0.2902846772544622, -0.9569403357322089}, // k=38 + {-0.3368898533922199, -0.9415440651830208}, // k=39 + {-0.3826834323650897, -0.9238795325112867}, // k=40 + {-0.4275550934302819, -0.9039892931234434}, // k=41 + {-0.4713967368259977, -0.8819212643483550}, // k=42 + {-0.5141027441932217, -0.8577286100002721}, // k=43 + {-0.5555702330196020, -0.8314696123025455}, // k=44 + {-0.5956993044924334, -0.8032075314806449}, // k=45 + {-0.6343932841636454, -0.7730104533627371}, // k=46 + {-0.6715589548470184, -0.7409511253549590}, // k=47 + {-0.7071067811865475, -0.7071067811865476}, // k=48 + {-0.7409511253549589, -0.6715589548470186}, // k=49 + {-0.7730104533627370, -0.6343932841636455}, // k=50 + {-0.8032075314806448, -0.5956993044924335}, // k=51 + {-0.8314696123025453, -0.5555702330196022}, // k=52 + {-0.8577286100002720, -0.5141027441932218}, // k=53 + {-0.8819212643483549, -0.4713967368259979}, // k=54 + {-0.9039892931234433, -0.4275550934302820}, // k=55 + {-0.9238795325112867, -0.3826834323650899}, // k=56 + {-0.9415440651830207, -0.3368898533922203}, // k=57 + {-0.9569403357322088, -0.2902846772544624}, // k=58 + {-0.9700312531945440, -0.2429801799032641}, // k=59 + {-0.9807852804032304, -0.1950903220161286}, // k=60 + {-0.9891765099647810, -0.1467304744553618}, // k=61 + {-0.9951847266721968, -0.0980171403295608}, // k=62 + {-0.9987954562051724, -0.0490676743274180}, // k=63 + {-1.0000000000000000, -0.0000000000000001}, // k=64 + {-0.9987954562051724, 0.0490676743274177}, // k=65 + {-0.9951847266721969, 0.0980171403295606}, // k=66 + {-0.9891765099647810, 0.1467304744553616}, // k=67 + {-0.9807852804032304, 0.1950903220161284}, // k=68 + {-0.9700312531945440, 0.2429801799032638}, // k=69 + {-0.9569403357322089, 0.2902846772544621}, // k=70 + {-0.9415440651830208, 0.3368898533922201}, // k=71 + {-0.9238795325112868, 0.3826834323650897}, // k=72 + {-0.9039892931234434, 0.4275550934302818}, // k=73 + {-0.8819212643483550, 0.4713967368259976}, // k=74 + {-0.8577286100002721, 0.5141027441932216}, // k=75 + {-0.8314696123025455, 0.5555702330196020}, // k=76 + {-0.8032075314806449, 0.5956993044924332}, // k=77 + {-0.7730104533627371, 0.6343932841636453}, // k=78 + {-0.7409511253549591, 0.6715589548470184}, // k=79 + {-0.7071067811865477, 0.7071067811865475}, // k=80 + {-0.6715589548470187, 0.7409511253549589}, // k=81 + {-0.6343932841636459, 0.7730104533627367}, // k=82 + {-0.5956993044924331, 0.8032075314806451}, // k=83 + {-0.5555702330196022, 0.8314696123025452}, // k=84 + {-0.5141027441932218, 0.8577286100002720}, // k=85 + {-0.4713967368259979, 0.8819212643483549}, // k=86 + {-0.4275550934302825, 0.9039892931234431}, // k=87 + {-0.3826834323650903, 0.9238795325112865}, // k=88 + {-0.3368898533922199, 0.9415440651830208}, // k=89 + {-0.2902846772544624, 0.9569403357322088}, // k=90 + {-0.2429801799032641, 0.9700312531945440}, // k=91 + {-0.1950903220161287, 0.9807852804032303}, // k=92 + {-0.1467304744553623, 0.9891765099647809}, // k=93 + {-0.0980171403295605, 0.9951847266721969}, // k=94 + {-0.0490676743274180, 0.9987954562051724}, // k=95 + {-0.0000000000000002, 1.0000000000000000}, // k=96 + {0.0490676743274177, 0.9987954562051724}, // k=97 + {0.0980171403295601, 0.9951847266721969}, // k=98 + {0.1467304744553619, 0.9891765099647809}, // k=99 + {0.1950903220161283, 0.9807852804032304}, // k=100 + {0.2429801799032638, 0.9700312531945440}, // k=101 + {0.2902846772544621, 0.9569403357322089}, // k=102 + {0.3368898533922196, 0.9415440651830209}, // k=103 + {0.3826834323650900, 0.9238795325112866}, // k=104 + {0.4275550934302821, 0.9039892931234433}, // k=105 + {0.4713967368259976, 0.8819212643483550}, // k=106 + {0.5141027441932216, 0.8577286100002722}, // k=107 + {0.5555702330196018, 0.8314696123025455}, // k=108 + {0.5956993044924329, 0.8032075314806453}, // k=109 + {0.6343932841636456, 0.7730104533627369}, // k=110 + {0.6715589548470183, 0.7409511253549591}, // k=111 + {0.7071067811865474, 0.7071067811865477}, // k=112 + {0.7409511253549589, 0.6715589548470187}, // k=113 + {0.7730104533627367, 0.6343932841636459}, // k=114 + {0.8032075314806451, 0.5956993044924332}, // k=115 + {0.8314696123025452, 0.5555702330196022}, // k=116 + {0.8577286100002720, 0.5141027441932219}, // k=117 + {0.8819212643483548, 0.4713967368259979}, // k=118 + {0.9039892931234431, 0.4275550934302825}, // k=119 + {0.9238795325112865, 0.3826834323650904}, // k=120 + {0.9415440651830208, 0.3368898533922200}, // k=121 + {0.9569403357322088, 0.2902846772544625}, // k=122 + {0.9700312531945440, 0.2429801799032642}, // k=123 + {0.9807852804032303, 0.1950903220161287}, // k=124 + {0.9891765099647809, 0.1467304744553624}, // k=125 + {0.9951847266721969, 0.0980171403295605}, // k=126 + {0.9987954562051724, 0.0490676743274181}, // k=127 +}; + +__constant__ double2 c_twiddle_256[256] = { + {1.0000000000000000, -0.0000000000000000}, // k=0 + {0.9996988186962042, -0.0245412285229123}, // k=1 + {0.9987954562051724, -0.0490676743274180}, // k=2 + {0.9972904566786902, -0.0735645635996674}, // k=3 + {0.9951847266721969, -0.0980171403295606}, // k=4 + {0.9924795345987100, -0.1224106751992162}, // k=5 + {0.9891765099647810, -0.1467304744553617}, // k=6 + {0.9852776423889412, -0.1709618887603012}, // k=7 + {0.9807852804032304, -0.1950903220161282}, // k=8 + {0.9757021300385286, -0.2191012401568698}, // k=9 + {0.9700312531945440, -0.2429801799032639}, // k=10 + {0.9637760657954398, -0.2667127574748984}, // k=11 + {0.9569403357322088, -0.2902846772544623}, // k=12 + {0.9495281805930367, -0.3136817403988915}, // k=13 + {0.9415440651830208, -0.3368898533922201}, // k=14 + {0.9329927988347390, -0.3598950365349881}, // k=15 + {0.9238795325112867, -0.3826834323650898}, // k=16 + {0.9142097557035307, -0.4052413140049899}, // k=17 + {0.9039892931234433, -0.4275550934302821}, // k=18 + {0.8932243011955153, -0.4496113296546065}, // k=19 + {0.8819212643483550, -0.4713967368259976}, // k=20 + {0.8700869911087115, -0.4928981922297840}, // k=21 + {0.8577286100002721, -0.5141027441932217}, // k=22 + {0.8448535652497071, -0.5349976198870972}, // k=23 + {0.8314696123025452, -0.5555702330196022}, // k=24 + {0.8175848131515837, -0.5758081914178453}, // k=25 + {0.8032075314806449, -0.5956993044924334}, // k=26 + {0.7883464276266063, -0.6152315905806268}, // k=27 + {0.7730104533627370, -0.6343932841636455}, // k=28 + {0.7572088465064846, -0.6531728429537768}, // k=29 + {0.7409511253549591, -0.6715589548470183}, // k=30 + {0.7242470829514670, -0.6895405447370668}, // k=31 + {0.7071067811865476, -0.7071067811865475}, // k=32 + {0.6895405447370669, -0.7242470829514669}, // k=33 + {0.6715589548470183, -0.7409511253549591}, // k=34 + {0.6531728429537768, -0.7572088465064845}, // k=35 + {0.6343932841636455, -0.7730104533627370}, // k=36 + {0.6152315905806268, -0.7883464276266062}, // k=37 + {0.5956993044924335, -0.8032075314806448}, // k=38 + {0.5758081914178453, -0.8175848131515837}, // k=39 + {0.5555702330196023, -0.8314696123025452}, // k=40 + {0.5349976198870973, -0.8448535652497070}, // k=41 + {0.5141027441932217, -0.8577286100002721}, // k=42 + {0.4928981922297841, -0.8700869911087113}, // k=43 + {0.4713967368259978, -0.8819212643483549}, // k=44 + {0.4496113296546066, -0.8932243011955153}, // k=45 + {0.4275550934302822, -0.9039892931234433}, // k=46 + {0.4052413140049899, -0.9142097557035307}, // k=47 + {0.3826834323650898, -0.9238795325112867}, // k=48 + {0.3598950365349883, -0.9329927988347388}, // k=49 + {0.3368898533922201, -0.9415440651830208}, // k=50 + {0.3136817403988916, -0.9495281805930367}, // k=51 + {0.2902846772544623, -0.9569403357322089}, // k=52 + {0.2667127574748984, -0.9637760657954398}, // k=53 + {0.2429801799032640, -0.9700312531945440}, // k=54 + {0.2191012401568698, -0.9757021300385286}, // k=55 + {0.1950903220161283, -0.9807852804032304}, // k=56 + {0.1709618887603014, -0.9852776423889412}, // k=57 + {0.1467304744553617, -0.9891765099647810}, // k=58 + {0.1224106751992163, -0.9924795345987100}, // k=59 + {0.0980171403295608, -0.9951847266721968}, // k=60 + {0.0735645635996675, -0.9972904566786902}, // k=61 + {0.0490676743274181, -0.9987954562051724}, // k=62 + {0.0245412285229123, -0.9996988186962042}, // k=63 + {0.0000000000000001, -1.0000000000000000}, // k=64 + {-0.0245412285229121, -0.9996988186962042}, // k=65 + {-0.0490676743274180, -0.9987954562051724}, // k=66 + {-0.0735645635996673, -0.9972904566786902}, // k=67 + {-0.0980171403295606, -0.9951847266721969}, // k=68 + {-0.1224106751992162, -0.9924795345987100}, // k=69 + {-0.1467304744553616, -0.9891765099647810}, // k=70 + {-0.1709618887603012, -0.9852776423889412}, // k=71 + {-0.1950903220161282, -0.9807852804032304}, // k=72 + {-0.2191012401568697, -0.9757021300385286}, // k=73 + {-0.2429801799032639, -0.9700312531945440}, // k=74 + {-0.2667127574748983, -0.9637760657954398}, // k=75 + {-0.2902846772544622, -0.9569403357322089}, // k=76 + {-0.3136817403988914, -0.9495281805930367}, // k=77 + {-0.3368898533922199, -0.9415440651830208}, // k=78 + {-0.3598950365349882, -0.9329927988347388}, // k=79 + {-0.3826834323650897, -0.9238795325112867}, // k=80 + {-0.4052413140049897, -0.9142097557035307}, // k=81 + {-0.4275550934302819, -0.9039892931234434}, // k=82 + {-0.4496113296546067, -0.8932243011955152}, // k=83 + {-0.4713967368259977, -0.8819212643483550}, // k=84 + {-0.4928981922297840, -0.8700869911087115}, // k=85 + {-0.5141027441932217, -0.8577286100002721}, // k=86 + {-0.5349976198870970, -0.8448535652497072}, // k=87 + {-0.5555702330196020, -0.8314696123025455}, // k=88 + {-0.5758081914178453, -0.8175848131515837}, // k=89 + {-0.5956993044924334, -0.8032075314806449}, // k=90 + {-0.6152315905806267, -0.7883464276266063}, // k=91 + {-0.6343932841636454, -0.7730104533627371}, // k=92 + {-0.6531728429537765, -0.7572088465064847}, // k=93 + {-0.6715589548470184, -0.7409511253549590}, // k=94 + {-0.6895405447370669, -0.7242470829514669}, // k=95 + {-0.7071067811865475, -0.7071067811865476}, // k=96 + {-0.7242470829514668, -0.6895405447370671}, // k=97 + {-0.7409511253549589, -0.6715589548470186}, // k=98 + {-0.7572088465064846, -0.6531728429537766}, // k=99 + {-0.7730104533627370, -0.6343932841636455}, // k=100 + {-0.7883464276266062, -0.6152315905806269}, // k=101 + {-0.8032075314806448, -0.5956993044924335}, // k=102 + {-0.8175848131515836, -0.5758081914178454}, // k=103 + {-0.8314696123025453, -0.5555702330196022}, // k=104 + {-0.8448535652497071, -0.5349976198870972}, // k=105 + {-0.8577286100002720, -0.5141027441932218}, // k=106 + {-0.8700869911087113, -0.4928981922297841}, // k=107 + {-0.8819212643483549, -0.4713967368259979}, // k=108 + {-0.8932243011955152, -0.4496113296546069}, // k=109 + {-0.9039892931234433, -0.4275550934302820}, // k=110 + {-0.9142097557035307, -0.4052413140049899}, // k=111 + {-0.9238795325112867, -0.3826834323650899}, // k=112 + {-0.9329927988347388, -0.3598950365349883}, // k=113 + {-0.9415440651830207, -0.3368898533922203}, // k=114 + {-0.9495281805930367, -0.3136817403988914}, // k=115 + {-0.9569403357322088, -0.2902846772544624}, // k=116 + {-0.9637760657954398, -0.2667127574748985}, // k=117 + {-0.9700312531945440, -0.2429801799032641}, // k=118 + {-0.9757021300385285, -0.2191012401568700}, // k=119 + {-0.9807852804032304, -0.1950903220161286}, // k=120 + {-0.9852776423889412, -0.1709618887603012}, // k=121 + {-0.9891765099647810, -0.1467304744553618}, // k=122 + {-0.9924795345987100, -0.1224106751992163}, // k=123 + {-0.9951847266721968, -0.0980171403295608}, // k=124 + {-0.9972904566786902, -0.0735645635996677}, // k=125 + {-0.9987954562051724, -0.0490676743274180}, // k=126 + {-0.9996988186962042, -0.0245412285229123}, // k=127 + {-1.0000000000000000, -0.0000000000000001}, // k=128 + {-0.9996988186962042, 0.0245412285229121}, // k=129 + {-0.9987954562051724, 0.0490676743274177}, // k=130 + {-0.9972904566786902, 0.0735645635996675}, // k=131 + {-0.9951847266721969, 0.0980171403295606}, // k=132 + {-0.9924795345987100, 0.1224106751992161}, // k=133 + {-0.9891765099647810, 0.1467304744553616}, // k=134 + {-0.9852776423889413, 0.1709618887603010}, // k=135 + {-0.9807852804032304, 0.1950903220161284}, // k=136 + {-0.9757021300385286, 0.2191012401568698}, // k=137 + {-0.9700312531945440, 0.2429801799032638}, // k=138 + {-0.9637760657954400, 0.2667127574748983}, // k=139 + {-0.9569403357322089, 0.2902846772544621}, // k=140 + {-0.9495281805930368, 0.3136817403988912}, // k=141 + {-0.9415440651830208, 0.3368898533922201}, // k=142 + {-0.9329927988347390, 0.3598950365349881}, // k=143 + {-0.9238795325112868, 0.3826834323650897}, // k=144 + {-0.9142097557035307, 0.4052413140049897}, // k=145 + {-0.9039892931234434, 0.4275550934302818}, // k=146 + {-0.8932243011955153, 0.4496113296546067}, // k=147 + {-0.8819212643483550, 0.4713967368259976}, // k=148 + {-0.8700869911087115, 0.4928981922297839}, // k=149 + {-0.8577286100002721, 0.5141027441932216}, // k=150 + {-0.8448535652497072, 0.5349976198870969}, // k=151 + {-0.8314696123025455, 0.5555702330196020}, // k=152 + {-0.8175848131515837, 0.5758081914178453}, // k=153 + {-0.8032075314806449, 0.5956993044924332}, // k=154 + {-0.7883464276266063, 0.6152315905806267}, // k=155 + {-0.7730104533627371, 0.6343932841636453}, // k=156 + {-0.7572088465064848, 0.6531728429537765}, // k=157 + {-0.7409511253549591, 0.6715589548470184}, // k=158 + {-0.7242470829514670, 0.6895405447370668}, // k=159 + {-0.7071067811865477, 0.7071067811865475}, // k=160 + {-0.6895405447370671, 0.7242470829514668}, // k=161 + {-0.6715589548470187, 0.7409511253549589}, // k=162 + {-0.6531728429537771, 0.7572088465064842}, // k=163 + {-0.6343932841636459, 0.7730104533627367}, // k=164 + {-0.6152315905806273, 0.7883464276266059}, // k=165 + {-0.5956993044924331, 0.8032075314806451}, // k=166 + {-0.5758081914178452, 0.8175848131515838}, // k=167 + {-0.5555702330196022, 0.8314696123025452}, // k=168 + {-0.5349976198870973, 0.8448535652497070}, // k=169 + {-0.5141027441932218, 0.8577286100002720}, // k=170 + {-0.4928981922297842, 0.8700869911087113}, // k=171 + {-0.4713967368259979, 0.8819212643483549}, // k=172 + {-0.4496113296546069, 0.8932243011955152}, // k=173 + {-0.4275550934302825, 0.9039892931234431}, // k=174 + {-0.4052413140049904, 0.9142097557035305}, // k=175 + {-0.3826834323650903, 0.9238795325112865}, // k=176 + {-0.3598950365349879, 0.9329927988347390}, // k=177 + {-0.3368898533922199, 0.9415440651830208}, // k=178 + {-0.3136817403988915, 0.9495281805930367}, // k=179 + {-0.2902846772544624, 0.9569403357322088}, // k=180 + {-0.2667127574748985, 0.9637760657954398}, // k=181 + {-0.2429801799032641, 0.9700312531945440}, // k=182 + {-0.2191012401568701, 0.9757021300385285}, // k=183 + {-0.1950903220161287, 0.9807852804032303}, // k=184 + {-0.1709618887603017, 0.9852776423889411}, // k=185 + {-0.1467304744553623, 0.9891765099647809}, // k=186 + {-0.1224106751992160, 0.9924795345987101}, // k=187 + {-0.0980171403295605, 0.9951847266721969}, // k=188 + {-0.0735645635996674, 0.9972904566786902}, // k=189 + {-0.0490676743274180, 0.9987954562051724}, // k=190 + {-0.0245412285229124, 0.9996988186962042}, // k=191 + {-0.0000000000000002, 1.0000000000000000}, // k=192 + {0.0245412285229120, 0.9996988186962042}, // k=193 + {0.0490676743274177, 0.9987954562051724}, // k=194 + {0.0735645635996670, 0.9972904566786902}, // k=195 + {0.0980171403295601, 0.9951847266721969}, // k=196 + {0.1224106751992156, 0.9924795345987101}, // k=197 + {0.1467304744553619, 0.9891765099647809}, // k=198 + {0.1709618887603013, 0.9852776423889412}, // k=199 + {0.1950903220161283, 0.9807852804032304}, // k=200 + {0.2191012401568697, 0.9757021300385286}, // k=201 + {0.2429801799032638, 0.9700312531945440}, // k=202 + {0.2667127574748982, 0.9637760657954400}, // k=203 + {0.2902846772544621, 0.9569403357322089}, // k=204 + {0.3136817403988911, 0.9495281805930368}, // k=205 + {0.3368898533922196, 0.9415440651830209}, // k=206 + {0.3598950365349876, 0.9329927988347391}, // k=207 + {0.3826834323650900, 0.9238795325112866}, // k=208 + {0.4052413140049900, 0.9142097557035306}, // k=209 + {0.4275550934302821, 0.9039892931234433}, // k=210 + {0.4496113296546066, 0.8932243011955153}, // k=211 + {0.4713967368259976, 0.8819212643483550}, // k=212 + {0.4928981922297839, 0.8700869911087115}, // k=213 + {0.5141027441932216, 0.8577286100002722}, // k=214 + {0.5349976198870969, 0.8448535652497072}, // k=215 + {0.5555702330196018, 0.8314696123025455}, // k=216 + {0.5758081914178449, 0.8175848131515840}, // k=217 + {0.5956993044924329, 0.8032075314806453}, // k=218 + {0.6152315905806270, 0.7883464276266061}, // k=219 + {0.6343932841636456, 0.7730104533627369}, // k=220 + {0.6531728429537768, 0.7572088465064846}, // k=221 + {0.6715589548470183, 0.7409511253549591}, // k=222 + {0.6895405447370668, 0.7242470829514670}, // k=223 + {0.7071067811865474, 0.7071067811865477}, // k=224 + {0.7242470829514667, 0.6895405447370672}, // k=225 + {0.7409511253549589, 0.6715589548470187}, // k=226 + {0.7572088465064842, 0.6531728429537771}, // k=227 + {0.7730104533627367, 0.6343932841636459}, // k=228 + {0.7883464276266059, 0.6152315905806274}, // k=229 + {0.8032075314806451, 0.5956993044924332}, // k=230 + {0.8175848131515837, 0.5758081914178452}, // k=231 + {0.8314696123025452, 0.5555702330196022}, // k=232 + {0.8448535652497070, 0.5349976198870973}, // k=233 + {0.8577286100002720, 0.5141027441932219}, // k=234 + {0.8700869911087113, 0.4928981922297843}, // k=235 + {0.8819212643483548, 0.4713967368259979}, // k=236 + {0.8932243011955151, 0.4496113296546070}, // k=237 + {0.9039892931234431, 0.4275550934302825}, // k=238 + {0.9142097557035305, 0.4052413140049904}, // k=239 + {0.9238795325112865, 0.3826834323650904}, // k=240 + {0.9329927988347390, 0.3598950365349880}, // k=241 + {0.9415440651830208, 0.3368898533922200}, // k=242 + {0.9495281805930367, 0.3136817403988915}, // k=243 + {0.9569403357322088, 0.2902846772544625}, // k=244 + {0.9637760657954398, 0.2667127574748986}, // k=245 + {0.9700312531945440, 0.2429801799032642}, // k=246 + {0.9757021300385285, 0.2191012401568702}, // k=247 + {0.9807852804032303, 0.1950903220161287}, // k=248 + {0.9852776423889411, 0.1709618887603018}, // k=249 + {0.9891765099647809, 0.1467304744553624}, // k=250 + {0.9924795345987100, 0.1224106751992160}, // k=251 + {0.9951847266721969, 0.0980171403295605}, // k=252 + {0.9972904566786902, 0.0735645635996674}, // k=253 + {0.9987954562051724, 0.0490676743274181}, // k=254 + {0.9996988186962042, 0.0245412285229124}, // k=255 +}; + +__constant__ double2 c_twiddle_512[512] = { + {1.0000000000000000, -0.0000000000000000}, // k=0 + {0.9999247018391445, -0.0122715382857199}, // k=1 + {0.9996988186962042, -0.0245412285229123}, // k=2 + {0.9993223845883495, -0.0368072229413588}, // k=3 + {0.9987954562051724, -0.0490676743274180}, // k=4 + {0.9981181129001492, -0.0613207363022086}, // k=5 + {0.9972904566786902, -0.0735645635996674}, // k=6 + {0.9963126121827780, -0.0857973123444399}, // k=7 + {0.9951847266721969, -0.0980171403295606}, // k=8 + {0.9939069700023561, -0.1102222072938831}, // k=9 + {0.9924795345987100, -0.1224106751992162}, // k=10 + {0.9909026354277800, -0.1345807085071262}, // k=11 + {0.9891765099647810, -0.1467304744553617}, // k=12 + {0.9873014181578584, -0.1588581433338614}, // k=13 + {0.9852776423889412, -0.1709618887603012}, // k=14 + {0.9831054874312163, -0.1830398879551410}, // k=15 + {0.9807852804032304, -0.1950903220161282}, // k=16 + {0.9783173707196277, -0.2071113761922186}, // k=17 + {0.9757021300385286, -0.2191012401568698}, // k=18 + {0.9729399522055602, -0.2310581082806711}, // k=19 + {0.9700312531945440, -0.2429801799032639}, // k=20 + {0.9669764710448521, -0.2548656596045146}, // k=21 + {0.9637760657954398, -0.2667127574748984}, // k=22 + {0.9604305194155658, -0.2785196893850531}, // k=23 + {0.9569403357322088, -0.2902846772544623}, // k=24 + {0.9533060403541939, -0.3020059493192281}, // k=25 + {0.9495281805930367, -0.3136817403988915}, // k=26 + {0.9456073253805213, -0.3253102921622629}, // k=27 + {0.9415440651830208, -0.3368898533922201}, // k=28 + {0.9373390119125750, -0.3484186802494346}, // k=29 + {0.9329927988347390, -0.3598950365349881}, // k=30 + {0.9285060804732156, -0.3713171939518375}, // k=31 + {0.9238795325112867, -0.3826834323650898}, // k=32 + {0.9191138516900578, -0.3939920400610481}, // k=33 + {0.9142097557035307, -0.4052413140049899}, // k=34 + {0.9091679830905224, -0.4164295600976372}, // k=35 + {0.9039892931234433, -0.4275550934302821}, // k=36 + {0.8986744656939538, -0.4386162385385277}, // k=37 + {0.8932243011955153, -0.4496113296546065}, // k=38 + {0.8876396204028539, -0.4605387109582400}, // k=39 + {0.8819212643483550, -0.4713967368259976}, // k=40 + {0.8760700941954066, -0.4821837720791227}, // k=41 + {0.8700869911087115, -0.4928981922297840}, // k=42 + {0.8639728561215868, -0.5035383837257176}, // k=43 + {0.8577286100002721, -0.5141027441932217}, // k=44 + {0.8513551931052652, -0.5245896826784689}, // k=45 + {0.8448535652497071, -0.5349976198870972}, // k=46 + {0.8382247055548381, -0.5453249884220465}, // k=47 + {0.8314696123025452, -0.5555702330196022}, // k=48 + {0.8245893027850253, -0.5657318107836131}, // k=49 + {0.8175848131515837, -0.5758081914178453}, // k=50 + {0.8104571982525948, -0.5857978574564389}, // k=51 + {0.8032075314806449, -0.5956993044924334}, // k=52 + {0.7958369046088836, -0.6055110414043255}, // k=53 + {0.7883464276266063, -0.6152315905806268}, // k=54 + {0.7807372285720945, -0.6248594881423863}, // k=55 + {0.7730104533627370, -0.6343932841636455}, // k=56 + {0.7651672656224590, -0.6438315428897914}, // k=57 + {0.7572088465064846, -0.6531728429537768}, // k=58 + {0.7491363945234594, -0.6624157775901718}, // k=59 + {0.7409511253549591, -0.6715589548470183}, // k=60 + {0.7326542716724128, -0.6806009977954530}, // k=61 + {0.7242470829514670, -0.6895405447370668}, // k=62 + {0.7157308252838186, -0.6983762494089729}, // k=63 + {0.7071067811865476, -0.7071067811865475}, // k=64 + {0.6983762494089729, -0.7157308252838186}, // k=65 + {0.6895405447370669, -0.7242470829514669}, // k=66 + {0.6806009977954531, -0.7326542716724128}, // k=67 + {0.6715589548470183, -0.7409511253549591}, // k=68 + {0.6624157775901718, -0.7491363945234593}, // k=69 + {0.6531728429537768, -0.7572088465064845}, // k=70 + {0.6438315428897915, -0.7651672656224590}, // k=71 + {0.6343932841636455, -0.7730104533627370}, // k=72 + {0.6248594881423865, -0.7807372285720944}, // k=73 + {0.6152315905806268, -0.7883464276266062}, // k=74 + {0.6055110414043255, -0.7958369046088835}, // k=75 + {0.5956993044924335, -0.8032075314806448}, // k=76 + {0.5857978574564389, -0.8104571982525948}, // k=77 + {0.5758081914178453, -0.8175848131515837}, // k=78 + {0.5657318107836132, -0.8245893027850253}, // k=79 + {0.5555702330196023, -0.8314696123025452}, // k=80 + {0.5453249884220465, -0.8382247055548380}, // k=81 + {0.5349976198870973, -0.8448535652497070}, // k=82 + {0.5245896826784688, -0.8513551931052652}, // k=83 + {0.5141027441932217, -0.8577286100002721}, // k=84 + {0.5035383837257176, -0.8639728561215867}, // k=85 + {0.4928981922297841, -0.8700869911087113}, // k=86 + {0.4821837720791228, -0.8760700941954066}, // k=87 + {0.4713967368259978, -0.8819212643483549}, // k=88 + {0.4605387109582400, -0.8876396204028539}, // k=89 + {0.4496113296546066, -0.8932243011955153}, // k=90 + {0.4386162385385277, -0.8986744656939538}, // k=91 + {0.4275550934302822, -0.9039892931234433}, // k=92 + {0.4164295600976373, -0.9091679830905223}, // k=93 + {0.4052413140049899, -0.9142097557035307}, // k=94 + {0.3939920400610481, -0.9191138516900578}, // k=95 + {0.3826834323650898, -0.9238795325112867}, // k=96 + {0.3713171939518376, -0.9285060804732155}, // k=97 + {0.3598950365349883, -0.9329927988347388}, // k=98 + {0.3484186802494345, -0.9373390119125750}, // k=99 + {0.3368898533922201, -0.9415440651830208}, // k=100 + {0.3253102921622630, -0.9456073253805213}, // k=101 + {0.3136817403988916, -0.9495281805930367}, // k=102 + {0.3020059493192282, -0.9533060403541938}, // k=103 + {0.2902846772544623, -0.9569403357322089}, // k=104 + {0.2785196893850531, -0.9604305194155658}, // k=105 + {0.2667127574748984, -0.9637760657954398}, // k=106 + {0.2548656596045146, -0.9669764710448521}, // k=107 + {0.2429801799032640, -0.9700312531945440}, // k=108 + {0.2310581082806713, -0.9729399522055601}, // k=109 + {0.2191012401568698, -0.9757021300385286}, // k=110 + {0.2071113761922186, -0.9783173707196277}, // k=111 + {0.1950903220161283, -0.9807852804032304}, // k=112 + {0.1830398879551411, -0.9831054874312163}, // k=113 + {0.1709618887603014, -0.9852776423889412}, // k=114 + {0.1588581433338614, -0.9873014181578584}, // k=115 + {0.1467304744553617, -0.9891765099647810}, // k=116 + {0.1345807085071262, -0.9909026354277800}, // k=117 + {0.1224106751992163, -0.9924795345987100}, // k=118 + {0.1102222072938832, -0.9939069700023561}, // k=119 + {0.0980171403295608, -0.9951847266721968}, // k=120 + {0.0857973123444399, -0.9963126121827780}, // k=121 + {0.0735645635996675, -0.9972904566786902}, // k=122 + {0.0613207363022086, -0.9981181129001492}, // k=123 + {0.0490676743274181, -0.9987954562051724}, // k=124 + {0.0368072229413590, -0.9993223845883495}, // k=125 + {0.0245412285229123, -0.9996988186962042}, // k=126 + {0.0122715382857199, -0.9999247018391445}, // k=127 + {0.0000000000000001, -1.0000000000000000}, // k=128 + {-0.0122715382857198, -0.9999247018391445}, // k=129 + {-0.0245412285229121, -0.9996988186962042}, // k=130 + {-0.0368072229413589, -0.9993223845883495}, // k=131 + {-0.0490676743274180, -0.9987954562051724}, // k=132 + {-0.0613207363022085, -0.9981181129001492}, // k=133 + {-0.0735645635996673, -0.9972904566786902}, // k=134 + {-0.0857973123444398, -0.9963126121827780}, // k=135 + {-0.0980171403295606, -0.9951847266721969}, // k=136 + {-0.1102222072938831, -0.9939069700023561}, // k=137 + {-0.1224106751992162, -0.9924795345987100}, // k=138 + {-0.1345807085071261, -0.9909026354277800}, // k=139 + {-0.1467304744553616, -0.9891765099647810}, // k=140 + {-0.1588581433338613, -0.9873014181578584}, // k=141 + {-0.1709618887603012, -0.9852776423889412}, // k=142 + {-0.1830398879551409, -0.9831054874312163}, // k=143 + {-0.1950903220161282, -0.9807852804032304}, // k=144 + {-0.2071113761922184, -0.9783173707196277}, // k=145 + {-0.2191012401568697, -0.9757021300385286}, // k=146 + {-0.2310581082806711, -0.9729399522055602}, // k=147 + {-0.2429801799032639, -0.9700312531945440}, // k=148 + {-0.2548656596045145, -0.9669764710448521}, // k=149 + {-0.2667127574748983, -0.9637760657954398}, // k=150 + {-0.2785196893850529, -0.9604305194155659}, // k=151 + {-0.2902846772544622, -0.9569403357322089}, // k=152 + {-0.3020059493192281, -0.9533060403541939}, // k=153 + {-0.3136817403988914, -0.9495281805930367}, // k=154 + {-0.3253102921622629, -0.9456073253805214}, // k=155 + {-0.3368898533922199, -0.9415440651830208}, // k=156 + {-0.3484186802494344, -0.9373390119125750}, // k=157 + {-0.3598950365349882, -0.9329927988347388}, // k=158 + {-0.3713171939518375, -0.9285060804732156}, // k=159 + {-0.3826834323650897, -0.9238795325112867}, // k=160 + {-0.3939920400610480, -0.9191138516900578}, // k=161 + {-0.4052413140049897, -0.9142097557035307}, // k=162 + {-0.4164295600976370, -0.9091679830905225}, // k=163 + {-0.4275550934302819, -0.9039892931234434}, // k=164 + {-0.4386162385385274, -0.8986744656939539}, // k=165 + {-0.4496113296546067, -0.8932243011955152}, // k=166 + {-0.4605387109582401, -0.8876396204028539}, // k=167 + {-0.4713967368259977, -0.8819212643483550}, // k=168 + {-0.4821837720791227, -0.8760700941954066}, // k=169 + {-0.4928981922297840, -0.8700869911087115}, // k=170 + {-0.5035383837257175, -0.8639728561215868}, // k=171 + {-0.5141027441932217, -0.8577286100002721}, // k=172 + {-0.5245896826784687, -0.8513551931052652}, // k=173 + {-0.5349976198870970, -0.8448535652497072}, // k=174 + {-0.5453249884220462, -0.8382247055548382}, // k=175 + {-0.5555702330196020, -0.8314696123025455}, // k=176 + {-0.5657318107836132, -0.8245893027850252}, // k=177 + {-0.5758081914178453, -0.8175848131515837}, // k=178 + {-0.5857978574564389, -0.8104571982525948}, // k=179 + {-0.5956993044924334, -0.8032075314806449}, // k=180 + {-0.6055110414043254, -0.7958369046088836}, // k=181 + {-0.6152315905806267, -0.7883464276266063}, // k=182 + {-0.6248594881423862, -0.7807372285720946}, // k=183 + {-0.6343932841636454, -0.7730104533627371}, // k=184 + {-0.6438315428897913, -0.7651672656224591}, // k=185 + {-0.6531728429537765, -0.7572088465064847}, // k=186 + {-0.6624157775901719, -0.7491363945234593}, // k=187 + {-0.6715589548470184, -0.7409511253549590}, // k=188 + {-0.6806009977954530, -0.7326542716724128}, // k=189 + {-0.6895405447370669, -0.7242470829514669}, // k=190 + {-0.6983762494089728, -0.7157308252838187}, // k=191 + {-0.7071067811865475, -0.7071067811865476}, // k=192 + {-0.7157308252838186, -0.6983762494089729}, // k=193 + {-0.7242470829514668, -0.6895405447370671}, // k=194 + {-0.7326542716724127, -0.6806009977954532}, // k=195 + {-0.7409511253549589, -0.6715589548470186}, // k=196 + {-0.7491363945234591, -0.6624157775901720}, // k=197 + {-0.7572088465064846, -0.6531728429537766}, // k=198 + {-0.7651672656224590, -0.6438315428897914}, // k=199 + {-0.7730104533627370, -0.6343932841636455}, // k=200 + {-0.7807372285720945, -0.6248594881423863}, // k=201 + {-0.7883464276266062, -0.6152315905806269}, // k=202 + {-0.7958369046088835, -0.6055110414043257}, // k=203 + {-0.8032075314806448, -0.5956993044924335}, // k=204 + {-0.8104571982525947, -0.5857978574564390}, // k=205 + {-0.8175848131515836, -0.5758081914178454}, // k=206 + {-0.8245893027850251, -0.5657318107836135}, // k=207 + {-0.8314696123025453, -0.5555702330196022}, // k=208 + {-0.8382247055548381, -0.5453249884220464}, // k=209 + {-0.8448535652497071, -0.5349976198870972}, // k=210 + {-0.8513551931052652, -0.5245896826784689}, // k=211 + {-0.8577286100002720, -0.5141027441932218}, // k=212 + {-0.8639728561215867, -0.5035383837257177}, // k=213 + {-0.8700869911087113, -0.4928981922297841}, // k=214 + {-0.8760700941954065, -0.4821837720791229}, // k=215 + {-0.8819212643483549, -0.4713967368259979}, // k=216 + {-0.8876396204028538, -0.4605387109582402}, // k=217 + {-0.8932243011955152, -0.4496113296546069}, // k=218 + {-0.8986744656939539, -0.4386162385385275}, // k=219 + {-0.9039892931234433, -0.4275550934302820}, // k=220 + {-0.9091679830905224, -0.4164295600976372}, // k=221 + {-0.9142097557035307, -0.4052413140049899}, // k=222 + {-0.9191138516900578, -0.3939920400610482}, // k=223 + {-0.9238795325112867, -0.3826834323650899}, // k=224 + {-0.9285060804732155, -0.3713171939518377}, // k=225 + {-0.9329927988347388, -0.3598950365349883}, // k=226 + {-0.9373390119125748, -0.3484186802494348}, // k=227 + {-0.9415440651830207, -0.3368898533922203}, // k=228 + {-0.9456073253805212, -0.3253102921622633}, // k=229 + {-0.9495281805930367, -0.3136817403988914}, // k=230 + {-0.9533060403541939, -0.3020059493192280}, // k=231 + {-0.9569403357322088, -0.2902846772544624}, // k=232 + {-0.9604305194155658, -0.2785196893850532}, // k=233 + {-0.9637760657954398, -0.2667127574748985}, // k=234 + {-0.9669764710448521, -0.2548656596045147}, // k=235 + {-0.9700312531945440, -0.2429801799032641}, // k=236 + {-0.9729399522055601, -0.2310581082806713}, // k=237 + {-0.9757021300385285, -0.2191012401568700}, // k=238 + {-0.9783173707196275, -0.2071113761922188}, // k=239 + {-0.9807852804032304, -0.1950903220161286}, // k=240 + {-0.9831054874312163, -0.1830398879551409}, // k=241 + {-0.9852776423889412, -0.1709618887603012}, // k=242 + {-0.9873014181578584, -0.1588581433338615}, // k=243 + {-0.9891765099647810, -0.1467304744553618}, // k=244 + {-0.9909026354277800, -0.1345807085071263}, // k=245 + {-0.9924795345987100, -0.1224106751992163}, // k=246 + {-0.9939069700023561, -0.1102222072938832}, // k=247 + {-0.9951847266721968, -0.0980171403295608}, // k=248 + {-0.9963126121827780, -0.0857973123444402}, // k=249 + {-0.9972904566786902, -0.0735645635996677}, // k=250 + {-0.9981181129001492, -0.0613207363022085}, // k=251 + {-0.9987954562051724, -0.0490676743274180}, // k=252 + {-0.9993223845883495, -0.0368072229413588}, // k=253 + {-0.9996988186962042, -0.0245412285229123}, // k=254 + {-0.9999247018391445, -0.0122715382857200}, // k=255 + {-1.0000000000000000, -0.0000000000000001}, // k=256 + {-0.9999247018391445, 0.0122715382857198}, // k=257 + {-0.9996988186962042, 0.0245412285229121}, // k=258 + {-0.9993223845883495, 0.0368072229413586}, // k=259 + {-0.9987954562051724, 0.0490676743274177}, // k=260 + {-0.9981181129001492, 0.0613207363022082}, // k=261 + {-0.9972904566786902, 0.0735645635996675}, // k=262 + {-0.9963126121827780, 0.0857973123444399}, // k=263 + {-0.9951847266721969, 0.0980171403295606}, // k=264 + {-0.9939069700023561, 0.1102222072938830}, // k=265 + {-0.9924795345987100, 0.1224106751992161}, // k=266 + {-0.9909026354277800, 0.1345807085071261}, // k=267 + {-0.9891765099647810, 0.1467304744553616}, // k=268 + {-0.9873014181578584, 0.1588581433338612}, // k=269 + {-0.9852776423889413, 0.1709618887603010}, // k=270 + {-0.9831054874312164, 0.1830398879551406}, // k=271 + {-0.9807852804032304, 0.1950903220161284}, // k=272 + {-0.9783173707196277, 0.2071113761922186}, // k=273 + {-0.9757021300385286, 0.2191012401568698}, // k=274 + {-0.9729399522055602, 0.2310581082806711}, // k=275 + {-0.9700312531945440, 0.2429801799032638}, // k=276 + {-0.9669764710448522, 0.2548656596045145}, // k=277 + {-0.9637760657954400, 0.2667127574748983}, // k=278 + {-0.9604305194155659, 0.2785196893850529}, // k=279 + {-0.9569403357322089, 0.2902846772544621}, // k=280 + {-0.9533060403541940, 0.3020059493192278}, // k=281 + {-0.9495281805930368, 0.3136817403988912}, // k=282 + {-0.9456073253805213, 0.3253102921622630}, // k=283 + {-0.9415440651830208, 0.3368898533922201}, // k=284 + {-0.9373390119125750, 0.3484186802494346}, // k=285 + {-0.9329927988347390, 0.3598950365349881}, // k=286 + {-0.9285060804732156, 0.3713171939518374}, // k=287 + {-0.9238795325112868, 0.3826834323650897}, // k=288 + {-0.9191138516900578, 0.3939920400610479}, // k=289 + {-0.9142097557035307, 0.4052413140049897}, // k=290 + {-0.9091679830905225, 0.4164295600976369}, // k=291 + {-0.9039892931234434, 0.4275550934302818}, // k=292 + {-0.8986744656939540, 0.4386162385385273}, // k=293 + {-0.8932243011955153, 0.4496113296546067}, // k=294 + {-0.8876396204028539, 0.4605387109582401}, // k=295 + {-0.8819212643483550, 0.4713967368259976}, // k=296 + {-0.8760700941954066, 0.4821837720791227}, // k=297 + {-0.8700869911087115, 0.4928981922297839}, // k=298 + {-0.8639728561215868, 0.5035383837257175}, // k=299 + {-0.8577286100002721, 0.5141027441932216}, // k=300 + {-0.8513551931052653, 0.5245896826784687}, // k=301 + {-0.8448535652497072, 0.5349976198870969}, // k=302 + {-0.8382247055548382, 0.5453249884220461}, // k=303 + {-0.8314696123025455, 0.5555702330196020}, // k=304 + {-0.8245893027850253, 0.5657318107836132}, // k=305 + {-0.8175848131515837, 0.5758081914178453}, // k=306 + {-0.8104571982525948, 0.5857978574564389}, // k=307 + {-0.8032075314806449, 0.5956993044924332}, // k=308 + {-0.7958369046088836, 0.6055110414043254}, // k=309 + {-0.7883464276266063, 0.6152315905806267}, // k=310 + {-0.7807372285720946, 0.6248594881423862}, // k=311 + {-0.7730104533627371, 0.6343932841636453}, // k=312 + {-0.7651672656224591, 0.6438315428897913}, // k=313 + {-0.7572088465064848, 0.6531728429537765}, // k=314 + {-0.7491363945234593, 0.6624157775901718}, // k=315 + {-0.7409511253549591, 0.6715589548470184}, // k=316 + {-0.7326542716724128, 0.6806009977954530}, // k=317 + {-0.7242470829514670, 0.6895405447370668}, // k=318 + {-0.7157308252838187, 0.6983762494089728}, // k=319 + {-0.7071067811865477, 0.7071067811865475}, // k=320 + {-0.6983762494089730, 0.7157308252838185}, // k=321 + {-0.6895405447370671, 0.7242470829514668}, // k=322 + {-0.6806009977954532, 0.7326542716724126}, // k=323 + {-0.6715589548470187, 0.7409511253549589}, // k=324 + {-0.6624157775901720, 0.7491363945234590}, // k=325 + {-0.6531728429537771, 0.7572088465064842}, // k=326 + {-0.6438315428897915, 0.7651672656224590}, // k=327 + {-0.6343932841636459, 0.7730104533627367}, // k=328 + {-0.6248594881423865, 0.7807372285720944}, // k=329 + {-0.6152315905806273, 0.7883464276266059}, // k=330 + {-0.6055110414043257, 0.7958369046088835}, // k=331 + {-0.5956993044924331, 0.8032075314806451}, // k=332 + {-0.5857978574564391, 0.8104571982525947}, // k=333 + {-0.5758081914178452, 0.8175848131515838}, // k=334 + {-0.5657318107836135, 0.8245893027850251}, // k=335 + {-0.5555702330196022, 0.8314696123025452}, // k=336 + {-0.5453249884220468, 0.8382247055548379}, // k=337 + {-0.5349976198870973, 0.8448535652497070}, // k=338 + {-0.5245896826784694, 0.8513551931052649}, // k=339 + {-0.5141027441932218, 0.8577286100002720}, // k=340 + {-0.5035383837257180, 0.8639728561215865}, // k=341 + {-0.4928981922297842, 0.8700869911087113}, // k=342 + {-0.4821837720791226, 0.8760700941954067}, // k=343 + {-0.4713967368259979, 0.8819212643483549}, // k=344 + {-0.4605387109582399, 0.8876396204028540}, // k=345 + {-0.4496113296546069, 0.8932243011955152}, // k=346 + {-0.4386162385385276, 0.8986744656939538}, // k=347 + {-0.4275550934302825, 0.9039892931234431}, // k=348 + {-0.4164295600976372, 0.9091679830905224}, // k=349 + {-0.4052413140049904, 0.9142097557035305}, // k=350 + {-0.3939920400610482, 0.9191138516900577}, // k=351 + {-0.3826834323650903, 0.9238795325112865}, // k=352 + {-0.3713171939518378, 0.9285060804732155}, // k=353 + {-0.3598950365349879, 0.9329927988347390}, // k=354 + {-0.3484186802494348, 0.9373390119125748}, // k=355 + {-0.3368898533922199, 0.9415440651830208}, // k=356 + {-0.3253102921622633, 0.9456073253805212}, // k=357 + {-0.3136817403988915, 0.9495281805930367}, // k=358 + {-0.3020059493192285, 0.9533060403541938}, // k=359 + {-0.2902846772544624, 0.9569403357322088}, // k=360 + {-0.2785196893850536, 0.9604305194155657}, // k=361 + {-0.2667127574748985, 0.9637760657954398}, // k=362 + {-0.2548656596045143, 0.9669764710448522}, // k=363 + {-0.2429801799032641, 0.9700312531945440}, // k=364 + {-0.2310581082806709, 0.9729399522055602}, // k=365 + {-0.2191012401568701, 0.9757021300385285}, // k=366 + {-0.2071113761922185, 0.9783173707196277}, // k=367 + {-0.1950903220161287, 0.9807852804032303}, // k=368 + {-0.1830398879551410, 0.9831054874312163}, // k=369 + {-0.1709618887603017, 0.9852776423889411}, // k=370 + {-0.1588581433338615, 0.9873014181578583}, // k=371 + {-0.1467304744553623, 0.9891765099647809}, // k=372 + {-0.1345807085071264, 0.9909026354277800}, // k=373 + {-0.1224106751992160, 0.9924795345987101}, // k=374 + {-0.1102222072938833, 0.9939069700023561}, // k=375 + {-0.0980171403295605, 0.9951847266721969}, // k=376 + {-0.0857973123444402, 0.9963126121827780}, // k=377 + {-0.0735645635996674, 0.9972904566786902}, // k=378 + {-0.0613207363022090, 0.9981181129001492}, // k=379 + {-0.0490676743274180, 0.9987954562051724}, // k=380 + {-0.0368072229413593, 0.9993223845883494}, // k=381 + {-0.0245412285229124, 0.9996988186962042}, // k=382 + {-0.0122715382857205, 0.9999247018391445}, // k=383 + {-0.0000000000000002, 1.0000000000000000}, // k=384 + {0.0122715382857201, 0.9999247018391445}, // k=385 + {0.0245412285229120, 0.9996988186962042}, // k=386 + {0.0368072229413590, 0.9993223845883495}, // k=387 + {0.0490676743274177, 0.9987954562051724}, // k=388 + {0.0613207363022086, 0.9981181129001492}, // k=389 + {0.0735645635996670, 0.9972904566786902}, // k=390 + {0.0857973123444399, 0.9963126121827780}, // k=391 + {0.0980171403295601, 0.9951847266721969}, // k=392 + {0.1102222072938829, 0.9939069700023561}, // k=393 + {0.1224106751992156, 0.9924795345987101}, // k=394 + {0.1345807085071260, 0.9909026354277800}, // k=395 + {0.1467304744553619, 0.9891765099647809}, // k=396 + {0.1588581433338612, 0.9873014181578584}, // k=397 + {0.1709618887603013, 0.9852776423889412}, // k=398 + {0.1830398879551406, 0.9831054874312164}, // k=399 + {0.1950903220161283, 0.9807852804032304}, // k=400 + {0.2071113761922181, 0.9783173707196278}, // k=401 + {0.2191012401568697, 0.9757021300385286}, // k=402 + {0.2310581082806706, 0.9729399522055603}, // k=403 + {0.2429801799032638, 0.9700312531945440}, // k=404 + {0.2548656596045140, 0.9669764710448523}, // k=405 + {0.2667127574748982, 0.9637760657954400}, // k=406 + {0.2785196893850533, 0.9604305194155658}, // k=407 + {0.2902846772544621, 0.9569403357322089}, // k=408 + {0.3020059493192281, 0.9533060403541939}, // k=409 + {0.3136817403988911, 0.9495281805930368}, // k=410 + {0.3253102921622629, 0.9456073253805213}, // k=411 + {0.3368898533922196, 0.9415440651830209}, // k=412 + {0.3484186802494345, 0.9373390119125750}, // k=413 + {0.3598950365349876, 0.9329927988347391}, // k=414 + {0.3713171939518374, 0.9285060804732156}, // k=415 + {0.3826834323650900, 0.9238795325112866}, // k=416 + {0.3939920400610479, 0.9191138516900579}, // k=417 + {0.4052413140049900, 0.9142097557035306}, // k=418 + {0.4164295600976369, 0.9091679830905225}, // k=419 + {0.4275550934302821, 0.9039892931234433}, // k=420 + {0.4386162385385273, 0.8986744656939540}, // k=421 + {0.4496113296546066, 0.8932243011955153}, // k=422 + {0.4605387109582396, 0.8876396204028542}, // k=423 + {0.4713967368259976, 0.8819212643483550}, // k=424 + {0.4821837720791222, 0.8760700941954069}, // k=425 + {0.4928981922297839, 0.8700869911087115}, // k=426 + {0.5035383837257178, 0.8639728561215866}, // k=427 + {0.5141027441932216, 0.8577286100002722}, // k=428 + {0.5245896826784691, 0.8513551931052651}, // k=429 + {0.5349976198870969, 0.8448535652497072}, // k=430 + {0.5453249884220465, 0.8382247055548380}, // k=431 + {0.5555702330196018, 0.8314696123025455}, // k=432 + {0.5657318107836131, 0.8245893027850253}, // k=433 + {0.5758081914178449, 0.8175848131515840}, // k=434 + {0.5857978574564388, 0.8104571982525949}, // k=435 + {0.5956993044924329, 0.8032075314806453}, // k=436 + {0.6055110414043253, 0.7958369046088837}, // k=437 + {0.6152315905806270, 0.7883464276266061}, // k=438 + {0.6248594881423861, 0.7807372285720946}, // k=439 + {0.6343932841636456, 0.7730104533627369}, // k=440 + {0.6438315428897912, 0.7651672656224592}, // k=441 + {0.6531728429537768, 0.7572088465064846}, // k=442 + {0.6624157775901715, 0.7491363945234596}, // k=443 + {0.6715589548470183, 0.7409511253549591}, // k=444 + {0.6806009977954527, 0.7326542716724131}, // k=445 + {0.6895405447370668, 0.7242470829514670}, // k=446 + {0.6983762494089724, 0.7157308252838190}, // k=447 + {0.7071067811865474, 0.7071067811865477}, // k=448 + {0.7157308252838188, 0.6983762494089727}, // k=449 + {0.7242470829514667, 0.6895405447370672}, // k=450 + {0.7326542716724129, 0.6806009977954530}, // k=451 + {0.7409511253549589, 0.6715589548470187}, // k=452 + {0.7491363945234594, 0.6624157775901718}, // k=453 + {0.7572088465064842, 0.6531728429537771}, // k=454 + {0.7651672656224588, 0.6438315428897915}, // k=455 + {0.7730104533627367, 0.6343932841636459}, // k=456 + {0.7807372285720944, 0.6248594881423865}, // k=457 + {0.7883464276266059, 0.6152315905806274}, // k=458 + {0.7958369046088833, 0.6055110414043257}, // k=459 + {0.8032075314806451, 0.5956993044924332}, // k=460 + {0.8104571982525947, 0.5857978574564391}, // k=461 + {0.8175848131515837, 0.5758081914178452}, // k=462 + {0.8245893027850251, 0.5657318107836136}, // k=463 + {0.8314696123025452, 0.5555702330196022}, // k=464 + {0.8382247055548377, 0.5453249884220468}, // k=465 + {0.8448535652497070, 0.5349976198870973}, // k=466 + {0.8513551931052649, 0.5245896826784694}, // k=467 + {0.8577286100002720, 0.5141027441932219}, // k=468 + {0.8639728561215864, 0.5035383837257181}, // k=469 + {0.8700869911087113, 0.4928981922297843}, // k=470 + {0.8760700941954067, 0.4821837720791226}, // k=471 + {0.8819212643483548, 0.4713967368259979}, // k=472 + {0.8876396204028539, 0.4605387109582399}, // k=473 + {0.8932243011955151, 0.4496113296546070}, // k=474 + {0.8986744656939538, 0.4386162385385277}, // k=475 + {0.9039892931234431, 0.4275550934302825}, // k=476 + {0.9091679830905224, 0.4164295600976373}, // k=477 + {0.9142097557035305, 0.4052413140049904}, // k=478 + {0.9191138516900577, 0.3939920400610483}, // k=479 + {0.9238795325112865, 0.3826834323650904}, // k=480 + {0.9285060804732155, 0.3713171939518378}, // k=481 + {0.9329927988347390, 0.3598950365349880}, // k=482 + {0.9373390119125748, 0.3484186802494349}, // k=483 + {0.9415440651830208, 0.3368898533922200}, // k=484 + {0.9456073253805212, 0.3253102921622634}, // k=485 + {0.9495281805930367, 0.3136817403988915}, // k=486 + {0.9533060403541936, 0.3020059493192286}, // k=487 + {0.9569403357322088, 0.2902846772544625}, // k=488 + {0.9604305194155657, 0.2785196893850537}, // k=489 + {0.9637760657954398, 0.2667127574748986}, // k=490 + {0.9669764710448522, 0.2548656596045144}, // k=491 + {0.9700312531945440, 0.2429801799032642}, // k=492 + {0.9729399522055602, 0.2310581082806710}, // k=493 + {0.9757021300385285, 0.2191012401568702}, // k=494 + {0.9783173707196277, 0.2071113761922185}, // k=495 + {0.9807852804032303, 0.1950903220161287}, // k=496 + {0.9831054874312163, 0.1830398879551410}, // k=497 + {0.9852776423889411, 0.1709618887603018}, // k=498 + {0.9873014181578583, 0.1588581433338616}, // k=499 + {0.9891765099647809, 0.1467304744553624}, // k=500 + {0.9909026354277800, 0.1345807085071264}, // k=501 + {0.9924795345987100, 0.1224106751992160}, // k=502 + {0.9939069700023561, 0.1102222072938834}, // k=503 + {0.9951847266721969, 0.0980171403295605}, // k=504 + {0.9963126121827780, 0.0857973123444403}, // k=505 + {0.9972904566786902, 0.0735645635996674}, // k=506 + {0.9981181129001492, 0.0613207363022091}, // k=507 + {0.9987954562051724, 0.0490676743274181}, // k=508 + {0.9993223845883494, 0.0368072229413594}, // k=509 + {0.9996988186962042, 0.0245412285229124}, // k=510 + {0.9999247018391445, 0.0122715382857206}, // k=511 +}; diff --git a/include/bout/utils.hxx b/include/bout/utils.hxx index e2ac814e53..3dbae60d74 100644 --- a/include/bout/utils.hxx +++ b/include/bout/utils.hxx @@ -5,10 +5,10 @@ * simple but common calculations * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * - * Contact: Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -29,13 +29,11 @@ #ifndef BOUT_UTILS_H #define BOUT_UTILS_H -#include "bout/build_config.hxx" - #include "bout/array.hxx" #include "bout/assert.hxx" #include "bout/bout_types.hxx" #include "bout/boutexception.hxx" -#include "bout/msg_stack.hxx" +#include "bout/build_config.hxx" #include "bout/region.hxx" #include "bout/unused.hxx" @@ -47,6 +45,11 @@ #include #include #include +#include +#include +#include + +class Field; #ifdef _MSC_VER // finite is not actually standard C++, it's a BSD extention for C @@ -187,6 +190,7 @@ typename std::vector::size_type erase_if(std::vector& c, Pre return r; } #else +using std::erase; using std::erase_if; #endif } // namespace utils @@ -231,6 +235,14 @@ public: data.reallocate(new_size_1 * new_size_2); } + /*! + * Change shape of the container. + * Invalidates contents. + */ + void reshape(std::tuple new_shape) { + reallocate(std::get<0>(new_shape), std::get<1>(new_shape)); + } + Matrix& operator=(const Matrix& other) { n1 = other.n1; n2 = other.n2; @@ -331,6 +343,14 @@ public: data.reallocate(new_size_1 * new_size_2 * new_size_3); } + /*! + * Change shape of the container. + * Invalidates contents. + */ + void reshape(std::tuple new_shape) { + reallocate(std::get<0>(new_shape), std::get<1>(new_shape), std::get<2>(new_shape)); + } + Tensor& operator=(const Tensor& other) { n1 = other.n1; n2 = other.n2; @@ -361,7 +381,6 @@ public: ASSERT2(0 <= i.ind && i.ind < n1 * n2 * n3); return data[i.ind]; } - T& operator[](Ind3D i) { // ny and nz are private :-( // ASSERT2(i.nz == n3); @@ -421,15 +440,13 @@ inline BoutReal randomu() { * Calculate the square of a variable \p t * i.e. t * t */ -template -inline T SQ(const T& t) { +template >>> +inline auto SQ(const T& t) { return t * t; } -template <> -BOUT_HOST_DEVICE inline BoutReal SQ(const BoutReal& t) { - return t * t; -} +BOUT_HOST_DEVICE inline BoutReal SQ(const BoutReal& t) { return t * t; } /*! * Round \p x to the nearest integer @@ -469,7 +486,7 @@ inline bool is_pow2(int x) { return x && !((x - 1) & x); } /*! * Return the sign of a number \p a - * by testing if a > 0 + * by testing if a > 0 */ template T SIGN(T a) { // Return +1 or -1 (0 -> +1) @@ -496,7 +513,7 @@ inline void checkData(BoutReal f) { } #else /// Ignored with disabled CHECK; Throw an exception if \p f is not finite -inline void checkData(BoutReal UNUSED(f)){}; +inline void checkData(BoutReal UNUSED(f)) {}; #endif /*! @@ -518,18 +535,18 @@ std::string toString(const T& val) { /// where the type may be std::string. inline std::string toString(const std::string& val) { return val; } -template <> -inline std::string toString<>(const Array& UNUSED(val)) { +template +inline std::string toString(const Array& UNUSED(val)) { return ""; } -template <> -inline std::string toString<>(const Matrix& UNUSED(val)) { +template +inline std::string toString(const Matrix& UNUSED(val)) { return ""; } -template <> -inline std::string toString<>(const Tensor& UNUSED(val)) { +template +inline std::string toString(const Tensor& UNUSED(val)) { return ""; } @@ -572,7 +589,7 @@ BoutReal stringToReal(const std::string& s); /*! * Convert a string to an int - * + * * Throws BoutException if can't be done */ int stringToInt(const std::string& s); @@ -589,7 +606,7 @@ std::list& strsplit(const std::string& s, char delim, /*! * Split a string on a given delimiter - * + * * @param[in] s The string to split (not modified by call) * @param[in] delim The delimiter to split on (single char) */ @@ -597,7 +614,7 @@ std::list strsplit(const std::string& s, char delim); /*! * Strips leading and trailing spaces from a string - * + * * @param[in] s The string to trim (not modified) * @param[in] c Collection of characters to remove */ @@ -605,7 +622,7 @@ std::string trim(const std::string& s, const std::string& c = " \t\r"); /*! * Strips leading spaces from a string - * + * * @param[in] s The string to trim (not modified) * @param[in] c Collection of characters to remove */ @@ -613,7 +630,7 @@ std::string trimLeft(const std::string& s, const std::string& c = " \t"); /*! * Strips leading spaces from a string - * + * * @param[in] s The string to trim (not modified) * @param[in] c Collection of characters to remove */ @@ -621,7 +638,7 @@ std::string trimRight(const std::string& s, const std::string& c = " \t\r"); /*! * Strips the comments from a string - * + * * @param[in] s The string to trim (not modified) * @param[in] c Collection of characters to remove */ diff --git a/include/bout/vector2d.hxx b/include/bout/vector2d.hxx index bdc375e698..1e19241a8e 100644 --- a/include/bout/vector2d.hxx +++ b/include/bout/vector2d.hxx @@ -13,7 +13,7 @@ * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -142,7 +142,7 @@ public: CELL_LOC getLocation() const override; // FieldData virtual functions - bool is3D() const override { return false; } + FieldType field_type() const override { return FieldType::field2d; } int elementSize() const override { return 3; } /// Apply boundary condition to all fields diff --git a/include/bout/vector3d.hxx b/include/bout/vector3d.hxx index 0c71dcffa5..73d3912838 100644 --- a/include/bout/vector3d.hxx +++ b/include/bout/vector3d.hxx @@ -3,13 +3,11 @@ * * \brief Class for 3D vectors. Built on the Field3D class. * - * \author B. Dudson, October 2007 - * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * - * Contact: Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -45,10 +43,10 @@ class Vector2D; * ------- * * Vector3D f; - * + * * a.x; // Error! a.x not allocated * - * + * */ class Vector3D : public FieldData { public: @@ -70,7 +68,7 @@ public: ~Vector3D() override; /*! - * The components of the vector. These can be + * The components of the vector. These can be * either co- or contra-variant, depending on * the boolean flag "covariant" */ @@ -92,8 +90,8 @@ public: bool covariant{true}; /*! - * In-place conversion to covariant form. - * + * In-place conversion to covariant form. + * * If already covariant (covariant = true) then does nothing * If contravariant, multiplies by metric tensor g_{ij} */ @@ -104,7 +102,7 @@ public: * * If already contravariant (covariant = false) then does nothing * If covariant, multiplies by metric tensor g^{ij} - * + * */ void toContravariant(); @@ -114,14 +112,14 @@ public: * The first time this is called, a new Vector3D object is created. * Subsequent calls return a pointer to this same object * - * For convenience, a standalone function "ddt" exists, so that + * For convenience, a standalone function "ddt" exists, so that * * ddt(v) is equivalent to *(v.timeDeriv()) - * + * * This does some book-keeping to ensure that the time derivative * of the components is the same as the components of the time derivative * - * ddt(v).x == ddt(v.x) + * ddt(v).x == ddt(v.x) */ Vector3D* timeDeriv(); @@ -172,7 +170,7 @@ public: CELL_LOC getLocation() const override; // FieldData virtual functions - bool is3D() const override { return true; } + FieldType field_type() const override { return FieldType::field3d; } int elementSize() const override { return 3; } void applyBoundary(bool init = false) override; @@ -203,7 +201,7 @@ const Vector3D cross(const Vector3D& lhs, const Vector2D& rhs); /*! * Absolute magnitude (modulus) of a vector |v| - * + * * sqrt( v.x^2 + v.y^2 + v.z^2 ) */ const Field3D abs(const Vector3D& v, const std::string& region = "RGN_ALL"); diff --git a/include/bout/yboundary_regions.hxx b/include/bout/yboundary_regions.hxx new file mode 100644 index 0000000000..b1aec6bc2e --- /dev/null +++ b/include/bout/yboundary_regions.hxx @@ -0,0 +1,154 @@ +#pragma once + +#include + +#include "./boundary_iterator.hxx" +#include "bout/assert.hxx" +#include "bout/boutexception.hxx" +#include "bout/field_data.hxx" +#include "bout/globals.hxx" +#include "bout/mask.hxx" +#include "bout/options.hxx" +#include "bout/parallel_boundary_region.hxx" +#include "bout/region.hxx" +#include +#include + +/// This class allows to simplify iterating over y-boundaries. +/// +/// It makes it easier to write code for FieldAligned boundaries, but if a bit +/// care is taken the code also works with FluxCoordinateIndependent code. +/// +/// An example how to replace old code is given here: +/// ../../manual/sphinx/user_docs/boundary_options.rst + +class YBoundary { +public: + template + void iter_regions(const Func& func) { + for (auto& region : boundary_regions) { + func(*region); + } + for (auto& region : boundary_regions_par) { + func(*region); + } + } + template + void iter_points(const Func& func) { + iter_regions([&](auto& region) { + for (auto& point : region) { + func(point); + } + }); + } + + template + void iter(const Func& func) { + return iter_points(func); + } + + YBoundary(YBndryType type, Options* options_ptr, const Mesh& mesh) { + bool lower_y = true; + bool upper_y = true; + bool outer_x = true; + bool inner_x = false; + if (options_ptr != nullptr) { + auto& options = *options_ptr; + if (!mesh.isFci()) { + lower_y = + options["lower_y"].doc("Boundary on lower y?").withDefault(lower_y); + upper_y = + options["upper_y"].doc("Boundary on upper y?").withDefault(upper_y); + } else { + outer_x = + options["outer_x"].doc("Boundary on outer x?").withDefault(outer_x); + inner_x = + options["inner_x"].doc("Boundary on inner x?").withDefault(inner_x); + } + } + switch (type) { + case YBndryType::sheath: + break; + case YBndryType::not_sheath: + lower_y = !lower_y; + upper_y = !upper_y; + outer_x = !outer_x; + inner_x = !inner_x; + break; + case YBndryType::all: + lower_y = true; + upper_y = true; + outer_x = true; + inner_x = true; + } + + if (mesh.isFci()) { + if (outer_x) { + for (auto& bndry : mesh.getBoundariesPar(BoundaryParType::xout)) { + boundary_regions_par.push_back(bndry); + } + } + if (inner_x) { + for (auto& bndry : mesh.getBoundariesPar(BoundaryParType::xin)) { + boundary_regions_par.push_back(bndry); + } + } + } else { + if (lower_y) { + boundary_regions.push_back( + std::make_shared(mesh, true, mesh.iterateBndryLowerY())); + } + if (upper_y) { + boundary_regions.push_back( + std::make_shared(mesh, false, mesh.iterateBndryUpperY())); + } + } + // Cache boundary regions + _contains.emplace_back(&mesh, false); + _contains.emplace_back(&mesh, false); + iter_points([&](const auto& point) { + if (point.dir() == 1) { + _contains[1][point.ind()] = true; + } else if (point.dir() == -1) { + _contains[0][point.ind()] = true; + } + }); + } + + bool contains_low(Ind3D ind) const { return _contains[0][ind]; } + bool contains_high(Ind3D ind) const { return _contains[1][ind]; } + template + bool contains(Ind3D ind) const { + static_assert(dir == 1 || dir == -1); + if constexpr (dir == 1) { + return _contains[1][ind]; + } + if constexpr (dir == -1) { + return _contains[0][ind]; + } + } + bool contains(int dir, Ind3D ind) const { + if (dir == 1) { + return contains<+1>(ind); + } + if (dir == -1) { + return contains<-1>(ind); + } + throw BoutException("only dir == 1 and dir == -1 are implemented, not {}", dir); + } + +private: + std::vector> boundary_regions_par; + std::vector> boundary_regions; + + std::vector _contains; +}; + +inline std::shared_ptr getYBoundary(Coordinates* coords, + YBndryType type = YBndryType::sheath) { + auto itype = static_cast(type); + if (coords->ybndrys[itype] == nullptr) { + coords->ybndrys[itype] = coords->makeYBoundary(type); + } + return coords->ybndrys[itype]; +} diff --git a/include/bout_types.hxx b/include/bout_types.hxx deleted file mode 100644 index e6048eb885..0000000000 --- a/include/bout_types.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "bout_types.hxx" has moved to "bout/bout_types.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/bout_types.hxx" diff --git a/include/boutcomm.hxx b/include/boutcomm.hxx deleted file mode 100644 index 256df49c4f..0000000000 --- a/include/boutcomm.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "boutcomm.hxx" has moved to "bout/boutcomm.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/boutcomm.hxx" diff --git a/include/boutexception.hxx b/include/boutexception.hxx deleted file mode 100644 index 9189babfb3..0000000000 --- a/include/boutexception.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "boutexception.hxx" has moved to "bout/boutexception.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/boutexception.hxx" diff --git a/include/cyclic_reduction.hxx b/include/cyclic_reduction.hxx deleted file mode 100644 index 3ff098b062..0000000000 --- a/include/cyclic_reduction.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "cyclic_reduction.hxx" has moved to "bout/cyclic_reduction.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/cyclic_reduction.hxx" diff --git a/include/dcomplex.hxx b/include/dcomplex.hxx deleted file mode 100644 index a91c5485bf..0000000000 --- a/include/dcomplex.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "dcomplex.hxx" has moved to "bout/dcomplex.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/dcomplex.hxx" diff --git a/include/derivs.hxx b/include/derivs.hxx deleted file mode 100644 index d0a6eb5831..0000000000 --- a/include/derivs.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "derivs.hxx" has moved to "bout/derivs.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/derivs.hxx" diff --git a/include/difops.hxx b/include/difops.hxx deleted file mode 100644 index a61d9712a9..0000000000 --- a/include/difops.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "difops.hxx" has moved to "bout/difops.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/difops.hxx" diff --git a/include/fft.hxx b/include/fft.hxx deleted file mode 100644 index d67b257388..0000000000 --- a/include/fft.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "fft.hxx" has moved to "bout/fft.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/fft.hxx" diff --git a/include/field.hxx b/include/field.hxx deleted file mode 100644 index a2fcb2464e..0000000000 --- a/include/field.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "field.hxx" has moved to "bout/field.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/field.hxx" diff --git a/include/field2d.hxx b/include/field2d.hxx deleted file mode 100644 index 3b148b3a50..0000000000 --- a/include/field2d.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "field2d.hxx" has moved to "bout/field2d.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/field2d.hxx" diff --git a/include/field3d.hxx b/include/field3d.hxx deleted file mode 100644 index 300cec057b..0000000000 --- a/include/field3d.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "field3d.hxx" has moved to "bout/field3d.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/field3d.hxx" diff --git a/include/field_data.hxx b/include/field_data.hxx deleted file mode 100644 index 90f5c96c40..0000000000 --- a/include/field_data.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "field_data.hxx" has moved to "bout/field_data.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/field_data.hxx" diff --git a/include/field_factory.hxx b/include/field_factory.hxx deleted file mode 100644 index c6302ee298..0000000000 --- a/include/field_factory.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "field_factory.hxx" has moved to "bout/field_factory.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/field_factory.hxx" diff --git a/include/fieldperp.hxx b/include/fieldperp.hxx deleted file mode 100644 index 1994e53060..0000000000 --- a/include/fieldperp.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "fieldperp.hxx" has moved to "bout/fieldperp.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/fieldperp.hxx" diff --git a/include/globals.hxx b/include/globals.hxx deleted file mode 100644 index f07488c10c..0000000000 --- a/include/globals.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "globals.hxx" has moved to "bout/globals.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/globals.hxx" diff --git a/include/gyro_average.hxx b/include/gyro_average.hxx deleted file mode 100644 index c0d454eb50..0000000000 --- a/include/gyro_average.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "gyro_average.hxx" has moved to "bout/gyro_average.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/gyro_average.hxx" diff --git a/include/initialprofiles.hxx b/include/initialprofiles.hxx deleted file mode 100644 index 7bc1595261..0000000000 --- a/include/initialprofiles.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "initialprofiles.hxx" has moved to "bout/initialprofiles.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/initialprofiles.hxx" diff --git a/include/interpolation.hxx b/include/interpolation.hxx deleted file mode 100644 index 80ad60b4e4..0000000000 --- a/include/interpolation.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "interpolation.hxx" has moved to "bout/interpolation.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/interpolation.hxx" diff --git a/include/interpolation_xz.hxx b/include/interpolation_xz.hxx deleted file mode 100644 index 8845e34b80..0000000000 --- a/include/interpolation_xz.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "interpolation_xz.hxx" has moved to "bout/interpolation_xz.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/interpolation_xz.hxx" diff --git a/include/interpolation_z.hxx b/include/interpolation_z.hxx deleted file mode 100644 index a88b486026..0000000000 --- a/include/interpolation_z.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "interpolation_z.hxx" has moved to "bout/interpolation_z.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/interpolation_z.hxx" diff --git a/include/invert_laplace.hxx b/include/invert_laplace.hxx deleted file mode 100644 index fbb04acaf1..0000000000 --- a/include/invert_laplace.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "invert_laplace.hxx" has moved to "bout/invert_laplace.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/invert_laplace.hxx" diff --git a/include/invert_parderiv.hxx b/include/invert_parderiv.hxx deleted file mode 100644 index 81a8ca4f49..0000000000 --- a/include/invert_parderiv.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "invert_parderiv.hxx" has moved to "bout/invert_parderiv.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/invert_parderiv.hxx" diff --git a/include/lapack_routines.hxx b/include/lapack_routines.hxx deleted file mode 100644 index 97fa2c6be9..0000000000 --- a/include/lapack_routines.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "lapack_routines.hxx" has moved to "bout/lapack_routines.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/lapack_routines.hxx" diff --git a/include/mask.hxx b/include/mask.hxx deleted file mode 100644 index cbeab65d85..0000000000 --- a/include/mask.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "mask.hxx" has moved to "bout/mask.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/mask.hxx" diff --git a/include/msg_stack.hxx b/include/msg_stack.hxx deleted file mode 100644 index 2503961748..0000000000 --- a/include/msg_stack.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "msg_stack.hxx" has moved to "bout/msg_stack.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/msg_stack.hxx" diff --git a/include/multiostream.hxx b/include/multiostream.hxx deleted file mode 100644 index 7e7101aad0..0000000000 --- a/include/multiostream.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "multiostream.hxx" has moved to "bout/multiostream.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/multiostream.hxx" diff --git a/include/options.hxx b/include/options.hxx deleted file mode 100644 index 4693422c13..0000000000 --- a/include/options.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "options.hxx" has moved to "bout/options.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/options.hxx" diff --git a/include/optionsreader.hxx b/include/optionsreader.hxx deleted file mode 100644 index 2c0f8344bf..0000000000 --- a/include/optionsreader.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "optionsreader.hxx" has moved to "bout/optionsreader.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/optionsreader.hxx" diff --git a/include/output.hxx b/include/output.hxx deleted file mode 100644 index 7eebcad478..0000000000 --- a/include/output.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "output.hxx" has moved to "bout/output.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/output.hxx" diff --git a/include/parallel_boundary_op.hxx b/include/parallel_boundary_op.hxx deleted file mode 100644 index e82a88d98f..0000000000 --- a/include/parallel_boundary_op.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "parallel_boundary_op.hxx" has moved to "bout/parallel_boundary_op.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/parallel_boundary_op.hxx" diff --git a/include/parallel_boundary_region.hxx b/include/parallel_boundary_region.hxx deleted file mode 100644 index dee2277007..0000000000 --- a/include/parallel_boundary_region.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "parallel_boundary_region.hxx" has moved to "bout/parallel_boundary_region.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/parallel_boundary_region.hxx" diff --git a/include/smoothing.hxx b/include/smoothing.hxx deleted file mode 100644 index b1a36adfb2..0000000000 --- a/include/smoothing.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "smoothing.hxx" has moved to "bout/smoothing.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/smoothing.hxx" diff --git a/include/sourcex.hxx b/include/sourcex.hxx deleted file mode 100644 index e09f3e89e4..0000000000 --- a/include/sourcex.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "sourcex.hxx" has moved to "bout/sourcex.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/sourcex.hxx" diff --git a/include/stencils.hxx b/include/stencils.hxx deleted file mode 100644 index fc877f3e1c..0000000000 --- a/include/stencils.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "stencils.hxx" has moved to "bout/stencils.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/stencils.hxx" diff --git a/include/unused.hxx b/include/unused.hxx deleted file mode 100644 index fa5ea7b6d1..0000000000 --- a/include/unused.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "unused.hxx" has moved to "bout/unused.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/unused.hxx" diff --git a/include/utils.hxx b/include/utils.hxx deleted file mode 100644 index d1a095e491..0000000000 --- a/include/utils.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "utils.hxx" has moved to "bout/utils.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/utils.hxx" diff --git a/include/vecops.hxx b/include/vecops.hxx deleted file mode 100644 index 1773418077..0000000000 --- a/include/vecops.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "vecops.hxx" has moved to "bout/vecops.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/vecops.hxx" diff --git a/include/vector2d.hxx b/include/vector2d.hxx deleted file mode 100644 index 6746d3f163..0000000000 --- a/include/vector2d.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "vector2d.hxx" has moved to "bout/vector2d.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/vector2d.hxx" diff --git a/include/vector3d.hxx b/include/vector3d.hxx deleted file mode 100644 index 747b6a75dd..0000000000 --- a/include/vector3d.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "vector3d.hxx" has moved to "bout/vector3d.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/vector3d.hxx" diff --git a/include/where.hxx b/include/where.hxx deleted file mode 100644 index e5b2bd9d30..0000000000 --- a/include/where.hxx +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// BOUT++ header shim -#warning Header "where.hxx" has moved to "bout/where.hxx". Run `bin/bout-v5-header-upgrader.py` to fix -#include "bout/where.hxx" diff --git a/locale/de/libbout.po b/locale/de/libbout.po index 0e0773b5db..38d9f62a3e 100644 --- a/locale/de/libbout.po +++ b/locale/de/libbout.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: BOUT++ 4.2.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-12 09:17+0100\n" +"POT-Creation-Date: 2025-08-13 23:37+0100\n" "PO-Revision-Date: 2019-02-06 17:32+0000\n" "Last-Translator: David \n" "Language-Team: German\n" @@ -18,132 +18,134 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.2.1\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:191 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:182 +#, c++-format msgid "" "\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" -"\t -> `Core` Region iyseps2_1-iyseps1_1 ({:d}-{:d} = {:d}) muss ein " -"Vielfaches von MYSUB ({:d}) sein\n" +"\t -> `Core` Region iyseps2_1-iyseps1_1 ({:d}-{:d} = {:d}) muss ein Vielfaches von MYSUB ({:d}) sein\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:224 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:215 +#, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" -"\t -> `Core` Region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) muss ein " -"Vielfaches von MYSUB ({:d}) sein\n" +"\t -> `Core` Region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) muss ein Vielfaches von MYSUB ({:d}) sein\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:199 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:190 +#, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" -"\t -> `Core` Region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) muss ein " -"Vielfaches von MYSUB ({:d}) sein\n" +"\t -> `Core` Region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) muss ein Vielfaches von MYSUB ({:d}) sein\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:309 +#: ../src/mesh/impls/bout/boutmesh.cxx:300 msgid "\t -> Good value\n" -msgstr "\t -> Wert OK\n" +msgstr "" +"\t -> Wert OK\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:180 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:171 +#, c++-format msgid "" "\t -> Leg region jyseps1_1+1 ({:d}) must be a multiple of MYSUB ({:d})\n" msgstr "" -"\t -> `Leg` Region jyseps1_1+1 ({:d}) muss ein Vielfaches von MYSUB ({:d}) " -"sein\n" +"\t -> `Leg` Region jyseps1_1+1 ({:d}) muss ein Vielfaches von MYSUB ({:d}) sein\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:215 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:206 +#, c++-format msgid "" "\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" -"\t -> `Leg` Region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) muss ein " -"Vielfaches von MYSUB ({:d}) sein\n" +"\t -> `Leg` Region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) muss ein Vielfaches von MYSUB ({:d}) sein\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:232 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:223 +#, c++-format msgid "" "\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a multiple of " "MYSUB ({:d})\n" msgstr "" -"\t -> `Leg` Region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) muss ein Vielfaches " -"von MYSUB ({:d}) sein\n" +"\t -> `Leg` Region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) muss ein Vielfaches von MYSUB ({:d}) sein\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:208 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:199 +#, c++-format msgid "" "\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" -"\t -> `Leg` Region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) muss ein " -"Vielfaches von MYSUB ({:d}) sein\n" +"\t -> `Leg` Region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) muss ein Vielfaches von MYSUB ({:d}) sein\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:175 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:166 +#, c++-format msgid "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n" -msgstr "\t -> ny/NYPE ({:d}/{:d} = {:d}) muss >= MYG ({:d}) sein\n" +msgstr "" +"\t -> ny/NYPE ({:d}/{:d} = {:d}) muss >= MYG ({:d}) sein\n" #: ../src/bout++.cxx:575 #, c++-format +msgid "\tADIOS2 support {}\n" +msgstr "" +"\tUnterstützung für ADIOS2 {}\n" + +#: ../src/bout++.cxx:583 +#, c++-format msgid "\tBacktrace in exceptions {}\n" msgstr "" +"\tBacktrace in Ausnahmen {}\n" #. Loop over all possibilities #. Processors divide equally #. Mesh in X divides equally #. Mesh in Y divides equally -#: ../src/mesh/impls/bout/boutmesh.cxx:297 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:288 +#, c++-format msgid "\tCandidate value: {:d}\n" -msgstr "\tzu überprüfender Wert: {:d}\n" +msgstr "" +"\tzu überprüfender Wert: {:d}\n" -#: ../src/bout++.cxx:576 +#: ../src/bout++.cxx:584 #, c++-format msgid "\tColour in logs {}\n" msgstr "" +"\tFarben in Protokollen {}\n" -#: ../src/bout++.cxx:594 +#: ../src/bout++.cxx:599 msgid "\tCommand line options for this run : " msgstr "\tKommandozeilenoptionen für diese Ausführung: " #. The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings #. which could cause problems (e.g. terminate strings). -#: ../src/bout++.cxx:590 -#, fuzzy, c++-format +#: ../src/bout++.cxx:595 +#, c++-format msgid "\tCompiled with flags : {:s}\n" -msgstr "\tWurde kompiliert mit den Optionen : {:s}\n" +msgstr "" +"\tWurde kompiliert mit den Optionen : {:s}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:324 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:315 +#, c++-format msgid "" "\tDomain split (NXPE={:d}, NYPE={:d}) into domains (localNx={:d}, " "localNy={:d})\n" msgstr "" -"\tDas Gebiet wird in NXPE={:d} mal NYPE={:d} Gebiete der Größe localNx={:d} " -"mal localNy={:d} aufgeteilt\n" +"\tDas Gebiet wird in NXPE={:d} mal NYPE={:d} Gebiete der Größe localNx={:d} mal localNy={:d} aufgeteilt\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:364 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:357 +#, c++-format msgid "\tERROR: Cannot split {:d} Y points equally between {:d} processors\n" msgstr "" -"\tFEHLER: {:d} Punkte in der Y-Richtung können nicht gleichmässig zwischen " -"{:d} Prozessen verteilt werden\n" +"\tFEHLER: {:d} Punkte in der Y-Richtung können nicht gleichmässig zwischen {:d} Prozessen verteilt werden\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:372 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:365 +#, c++-format msgid "\tERROR: Cannot split {:d} Z points equally between {:d} processors\n" msgstr "" -"\tFEHLER: {:d} Punkte in der Y-Richtung können nicht gleichmässig zwischen " -"{:d} Prozessen verteilt werden\n" +"\tFEHLER: {:d} Punkte in der Y-Richtung können nicht gleichmässig zwischen {:d} Prozessen verteilt werden\n" #: ../src/sys/options/options_ini.cxx:200 -#, fuzzy, c++-format +#, c++-format msgid "" "\tEmpty key\n" "\tLine: {:s}" @@ -152,41 +154,45 @@ msgstr "" "\tZeile: {:s}" #: ../src/sys/optionsreader.cxx:127 -#, fuzzy, c++-format +#, c++-format msgid "\tEmpty key or value in command line '{:s}'\n" -msgstr "\tSchlüssel (Key) oder Wert nicht gesetzt in der Befehlszeile '{:s}'\n" +msgstr "" +"\tSchlüssel (Key) oder Wert nicht gesetzt in der Befehlszeile '{:s}'\n" -#: ../src/bout++.cxx:582 +#: ../src/bout++.cxx:587 #, c++-format msgid "\tExtra debug output {}\n" msgstr "" +"\tZusätzliche Debug-Ausgabe {}\n" -#: ../src/bout++.cxx:561 -#, fuzzy, c++-format +#: ../src/bout++.cxx:568 +#, c++-format msgid "\tFFT support {}\n" -msgstr "\tNetCDF Unterstützung ist aktiviert\n" +msgstr "" +"\tUnterstützung für FFT {}\n" -#: ../src/bout++.cxx:585 +#: ../src/bout++.cxx:590 #, c++-format msgid "\tField name tracking {}\n" msgstr "" +"\tVerfolgung von Feldnamen {}\n" -#: ../src/bout++.cxx:583 +#: ../src/bout++.cxx:588 #, c++-format msgid "\tFloating-point exceptions {}\n" msgstr "" +"\tGleitkomma-Ausnahmen {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:440 +#: ../src/mesh/impls/bout/boutmesh.cxx:476 msgid "\tGrid size: " msgstr "\tGittergröße: " -#: ../src/mesh/impls/bout/boutmesh.cxx:463 -#, fuzzy +#: ../src/mesh/impls/bout/boutmesh.cxx:499 msgid "\tGuard cells (x,y,z): " -msgstr "\tGuardzellen (x,y): " +msgstr "\tGuard-Zellen (x,y,z): " #: ../src/sys/options/options_ini.cxx:204 -#, fuzzy, c++-format +#, c++-format msgid "" "\tKey must not contain ':' character\n" "\tLine: {:s}" @@ -194,139 +200,156 @@ msgstr "" "\tDer Schlüssel darf nicht ':' enthalten\n" "\tZeile: {:s}" -#: ../src/bout++.cxx:563 +#: ../src/bout++.cxx:570 #, c++-format msgid "\tLAPACK support {}\n" msgstr "" +"\tUnterstützung für LAPACK {}\n" -#: ../src/bout++.cxx:586 +#: ../src/bout++.cxx:591 #, c++-format msgid "\tMessage stack {}\n" msgstr "" +"\tNachrichten-Stack {}\n" -#: ../src/bout++.cxx:560 +#: ../src/bout++.cxx:567 #, c++-format msgid "\tMetrics mode is {}\n" msgstr "" +"\tMetrikmodus ist {}\n" #: ../src/sys/optionsreader.cxx:111 -#, fuzzy, c++-format +#, c++-format msgid "\tMultiple '=' in command-line argument '{:s}'\n" -msgstr "\t'=' darf nicht mehrfach vorkommen: '{:s}'\n" +msgstr "" +"\t'=' darf nicht mehrfach vorkommen: '{:s}'\n" -#: ../src/bout++.cxx:562 +#: ../src/bout++.cxx:569 #, c++-format msgid "\tNatural language support {}\n" msgstr "" +"\tUnterstützung für natürliche Sprache {}\n" -#: ../src/bout++.cxx:567 -#, fuzzy, c++-format +#: ../src/bout++.cxx:574 +#, c++-format msgid "\tNetCDF support {}{}\n" -msgstr "\tNetCDF Unterstützung ist aktiviert\n" +msgstr "" +"\tUnterstützung für NetCDF {}{}\n" -#: ../src/bout++.cxx:577 -#, fuzzy, c++-format -msgid "\tOpenMP parallelisation {}" -msgstr "\tOpenMP Parallelisierung ist deaktiviert\n" +#: ../src/bout++.cxx:585 +#, c++-format +msgid "\tOpenMP parallelisation {}, using {} threads\n" +msgstr "" +"\tOpenMP-Parallelisierung {}, mit {} Threads\n" #. Mark the option as used #. Option not found -#: ../src/sys/options.cxx:311 ../src/sys/options.cxx:380 -#: ../src/sys/options.cxx:415 ../src/sys/options.cxx:457 -#: ../src/sys/options.cxx:717 ../src/sys/options.cxx:744 -#: ../src/sys/options.cxx:771 ../include/bout/options.hxx:516 -#: ../include/bout/options.hxx:549 ../include/bout/options.hxx:573 -#: ../include/bout/options.hxx:820 +#: ../include/bout/options.hxx:586 ../include/bout/options.hxx:619 +#: ../include/bout/options.hxx:643 ../include/bout/options.hxx:896 msgid "\tOption " msgstr "\tOption " -#: ../src/sys/options.cxx:447 -#, fuzzy, c++-format -msgid "\tOption '{:s}': Boolean expected. Got '{:s}'\n" -msgstr "\tOption '{:s}': Boolscherwert erwartet, '{:s}' gefunden\n" +#: ../src/sys/options.cxx:369 +#, c++-format +msgid "\tOption {} = {}" +msgstr "\tOption {} = {}" #: ../src/sys/options/options_ini.cxx:70 -#, fuzzy, c++-format +#, c++-format msgid "\tOptions file '{:s}' not found\n" -msgstr "\tDie Optionendatei '{:s}' konnte nicht gefunden werden\n" +msgstr "" +"\tDie Optionendatei '{:s}' konnte nicht gefunden werden\n" -#: ../src/bout++.cxx:568 +#: ../src/bout++.cxx:576 #, c++-format msgid "\tPETSc support {}\n" msgstr "" +"\tUnterstützung für PETSc {}\n" -#: ../src/bout++.cxx:571 +#: ../src/bout++.cxx:579 #, c++-format msgid "\tPVODE support {}\n" msgstr "" +"\tUnterstützung für PVODE {}\n" -#: ../src/bout++.cxx:557 +#: ../src/bout++.cxx:564 msgid "\tParallel NetCDF support disabled\n" -msgstr "\tParallele-NetCDF Unterstützung ist deaktiviert\n" +msgstr "" +"\tParallele-NetCDF Unterstützung ist deaktiviert\n" -#: ../src/bout++.cxx:555 +#: ../src/bout++.cxx:562 msgid "\tParallel NetCDF support enabled\n" -msgstr "\tParllele-NetCDF Unterstützung ist aktiviert\n" +msgstr "" +"\tParllele-NetCDF Unterstützung ist aktiviert\n" -#: ../src/bout++.cxx:569 +#: ../src/bout++.cxx:577 #, c++-format msgid "\tPretty function name support {}\n" msgstr "" +"\tUnterstützung für schönere Funktionsnamen {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:437 +#: ../src/mesh/impls/bout/boutmesh.cxx:473 msgid "\tRead nz from input grid file\n" -msgstr "\tnz wird von der Griddatei gelesen\n" +msgstr "" +"\tnz wird von der Griddatei gelesen\n" -#: ../src/mesh/mesh.cxx:238 +#: ../src/mesh/mesh.cxx:249 msgid "\tReading contravariant vector " msgstr "\tKontravariantevektoren werden gelesen " -#: ../src/mesh/mesh.cxx:231 ../src/mesh/mesh.cxx:252 +#: ../src/mesh/mesh.cxx:242 ../src/mesh/mesh.cxx:263 msgid "\tReading covariant vector " msgstr "\tKovariantevektoren werden gelesen " -#: ../src/bout++.cxx:548 +#: ../src/bout++.cxx:555 #, c++-format msgid "\tRuntime error checking {}" -msgstr "" +msgstr "\tPrüfung auf Laufzeitfehler {}" -#: ../src/bout++.cxx:573 +#: ../src/bout++.cxx:581 #, c++-format msgid "\tSLEPc support {}\n" msgstr "" +"\tUnterstützung für SLEPc {}\n" -#: ../src/bout++.cxx:574 +#: ../src/bout++.cxx:582 #, c++-format msgid "\tSUNDIALS support {}\n" msgstr "" +"\tUnterstützung für SUNDIALS {}\n" -#: ../src/bout++.cxx:572 +#: ../src/bout++.cxx:580 #, c++-format msgid "\tScore-P support {}\n" msgstr "" +"\tUnterstützung für Score-P {}\n" -#: ../src/bout++.cxx:584 -#, fuzzy, c++-format +#: ../src/bout++.cxx:589 +#, c++-format msgid "\tSignal handling support {}\n" -msgstr "\tSignalverarbeitung ist deaktiviert\n" +msgstr "" +"\tUnterstützung für Signalbehandlung {}\n" #: ../src/solver/impls/split-rk/split-rk.cxx:76 #, c++-format msgid "\tUsing a timestep {:e}\n" msgstr "" +"\tVerwende einen Zeitschritt von {:e}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:577 +#: ../src/mesh/impls/bout/boutmesh.cxx:613 msgid "\tdone\n" -msgstr "\tfertig\n" +msgstr "" +"\tfertig\n" #: ../src/solver/impls/split-rk/split-rk.cxx:41 msgid "" "\n" "\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n" msgstr "" +"\n" +"\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n" -#: ../src/bout++.cxx:371 -#, fuzzy +#: ../src/bout++.cxx:378 msgid "" "\n" " -d \t\tLook in for input/output files\n" @@ -339,22 +362,24 @@ msgstr "" "\n" " -d \tEin- und Ausgabedateien sind im \n" " -f \tOptinen werden aus der gelesen\n" -" -o \tGenutzte Optionen werden in der " -" gespeichert\n" +" -o \tGenutzte Optionen werden in der gespeichert\n" " -l, --log \tSchreibe das Log in die \n" " -v, --verbose\t\tWortreicherer Ausgabe\n" " -q, --quiet\t\tNur wichtigere Ausgaben anzeigen\n" -#: ../src/sys/expressionparser.cxx:302 +#: ../src/sys/expressionparser.cxx:341 #, c++-format msgid "" "\n" " {1: ^{2}}{0}\n" " Did you mean '{0}'?" msgstr "" +"\n" +" {1: ^{2}}{0}\n" +" Meinten Sie '{0}'?" -#: ../src/solver/solver.cxx:580 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:586 +#, c++-format msgid "" "\n" "Run finished at : {:s}\n" @@ -362,8 +387,8 @@ msgstr "" "\n" "Simulation beendet um {:s}\n" -#: ../src/solver/solver.cxx:532 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:540 +#, c++-format msgid "" "\n" "Run started at : {:s}\n" @@ -374,7 +399,7 @@ msgstr "" #. Raw string to help with the formatting of the message, and a #. separate variable so clang-format doesn't barf on the #. exception -#: ../src/sys/options.cxx:1102 +#: ../src/sys/options.cxx:1158 msgid "" "\n" "There were unused input options:\n" @@ -401,8 +426,32 @@ msgid "" "\n" "{}" msgstr "" +"\n" +"There were unused input options:\n" +"-----\n" +"{:i}\n" +"-----\n" +"It's possible you've mistyped some options. BOUT++ input arguments are\n" +"now case-sensitive, and some have changed name. You can try running\n" +"\n" +" /bin/bout-v5-input-file-upgrader.py {}/{}\n" +"\n" +"to automatically fix the most common issues. If these options above\n" +"are sometimes used depending on other options, you can call\n" +"`Options::setConditionallyUsed()`, for example:\n" +"\n" +" Options::root()[\"{}\"].setConditionallyUsed();\n" +"\n" +"to mark a section or value as depending on other values, and so ignore\n" +"it in this check. Alternatively, if you're sure the above inputs are\n" +"not a mistake, you can set 'input:error_on_unused_options=false' to\n" +"turn off this check for unused options. You can always set\n" +"'input:validate=true' to check inputs without running the full\n" +"simulation.\n" +"\n" +"{}" -#: ../src/bout++.cxx:382 +#: ../src/bout++.cxx:389 #, c++-format msgid "" " --print-config\t\tPrint the compile-time configuration\n" @@ -434,62 +483,84 @@ msgid "" "For all possible input parameters, see the user manual and/or the physics " "model source (e.g. {:s}.cxx)\n" msgstr "" +" --print-config\t\tPrint the compile-time configuration\n" +" --list-solvers\t\tList the available time solvers\n" +" --help-solver \tPrint help for the given time solver\n" +" --list-laplacians\t\tList the available Laplacian inversion solvers\n" +" --help-laplacian \tPrint help for the given Laplacian inversion solver\n" +" --list-laplacexz\t\tList the available LaplaceXZ inversion solvers\n" +" --help-laplacexz \tPrint help for the given LaplaceXZ inversion solver\n" +" --list-invertpars\t\tList the available InvertPar solvers\n" +" --help-invertpar \tPrint help for the given InvertPar solver\n" +" --list-rkschemes\t\tList the available Runge-Kutta schemes\n" +" --help-rkscheme \tPrint help for the given Runge-Kutta scheme\n" +" --list-meshes\t\t\tList the available Meshes\n" +" --help-mesh \t\tPrint help for the given Mesh\n" +" --list-xzinterpolations\tList the available XZInterpolations\n" +" --help-xzinterpolation \tPrint help for the given XZInterpolation\n" +" --list-zinterpolations\tList the available ZInterpolations\n" +" --help-zinterpolation \tPrint help for the given ZInterpolation\n" +" -h, --help\t\t\tThis message\n" +" restart [append]\t\tRestart the simulation. If append is specified, append to the existing output files, otherwise overwrite them\n" +" VAR=VALUE\t\t\tSpecify a VALUE for input parameter VAR\n" +"\n" +"For all possible input parameters, see the user manual and/or the physics model source (e.g. {:s}.cxx)\n" -#: ../src/bout++.cxx:379 -#, fuzzy +#: ../src/bout++.cxx:386 msgid " -c, --color\t\t\tColor output using bout-log-color\n" -msgstr " -c, --color\t\tFarbliche Ausgabe mit bout-log-color\n" +msgstr "" +" -c, --color\t\tFarbliche Ausgabe mit bout-log-color\n" -#: ../include/bout/options.hxx:823 -#, fuzzy +#: ../include/bout/options.hxx:899 msgid ") overwritten with:" -msgstr ") überschrieben mit {:s}" +msgstr ") überschrieben mit:" -#: ../src/bout++.cxx:550 +#: ../src/bout++.cxx:557 #, c++-format msgid ", level {}" -msgstr "" - -#: ../src/bout++.cxx:579 -#, c++-format -msgid ", using {} threads" -msgstr "" +msgstr ", level {}" #: ../tests/unit/src/test_bout++.cxx:352 msgid "4 of 8" -msgstr "" +msgstr "4 of 8" -#: ../src/sys/options.cxx:868 +#: ../src/sys/options.cxx:895 msgid "All options used\n" -msgstr "Alle genutzten Optionen\n" +msgstr "" +"Alle genutzten Optionen\n" -#: ../src/bout++.cxx:528 -#, fuzzy, c++-format +#: ../src/bout++.cxx:535 +#, c++-format msgid "BOUT++ version {:s}\n" -msgstr "BOUT++ Version {:s}\n" +msgstr "" +"BOUT++ Version {:s}\n" -#: ../src/bout++.cxx:143 -#, fuzzy +#: ../src/bout++.cxx:147 msgid "Bad command line arguments:\n" -msgstr "\t'=' darf nicht mehrfach vorkommen: '{:s}'\n" +msgstr "" +"Bad command line arguments:\n" + +#: ../src/sys/expressionparser.cxx:192 +#, c++-format +msgid "Boolean operator argument {:e} is not a bool" +msgstr "Boolean operator argument {:e} is not a bool" -#: ../src/mesh/impls/bout/boutmesh.cxx:559 +#: ../src/mesh/impls/bout/boutmesh.cxx:595 msgid "Boundary regions in this processor: " msgstr "Randgebiete auf diesem Prozessor: " -#: ../src/mesh/impls/bout/boutmesh.cxx:355 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:348 +#, c++-format msgid "Cannot split {:d} X points equally between {:d} processors\n" msgstr "" -"{:d} Punkte in der X-Richtung können nicht gleichmässig zwischen {:d} " -"Prozessen verteilt werden\n" +"{:d} Punkte in der X-Richtung können nicht gleichmässig zwischen {:d} Prozessen verteilt werden\n" -#: ../src/bout++.cxx:818 +#: ../src/bout++.cxx:829 msgid "Check if a file exists, and exit if it does." -msgstr "" +msgstr "Check if a file exists, and exit if it does." -#: ../src/bout++.cxx:533 -#, fuzzy, c++-format +#: ../src/bout++.cxx:540 +#, c++-format msgid "" "Code compiled on {:s} at {:s}\n" "\n" @@ -501,62 +572,59 @@ msgstr "" msgid "Command line" msgstr "Befehlszeile" -#: ../src/bout++.cxx:544 ../tests/unit/src/test_bout++.cxx:358 +#: ../src/bout++.cxx:551 ../tests/unit/src/test_bout++.cxx:358 msgid "Compile-time options:\n" -msgstr "Kompiliert mit:\n" +msgstr "" +"Kompiliert mit:\n" #: ../tests/unit/src/test_bout++.cxx:362 -#, fuzzy msgid "Compiled with flags" -msgstr "\tWurde kompiliert mit den Optionen : {:s}\n" +msgstr "Compiled with flags" -#: ../src/mesh/impls/bout/boutmesh.cxx:568 +#: ../src/mesh/impls/bout/boutmesh.cxx:604 msgid "Constructing default regions" msgstr "Standardregionen werden erstellt" -#: ../src/bout++.cxx:520 -#, fuzzy, c++-format +#: ../src/bout++.cxx:527 +#, c++-format msgid "Could not create PID file {:s}" -msgstr "Die Ausgabedatei '{:s}' konnte nicht geöffnet werden\n" +msgstr "PID-Datei {:s} konnte nicht erstellt werden" -#: ../src/mesh/impls/bout/boutmesh.cxx:318 +#: ../src/mesh/impls/bout/boutmesh.cxx:309 msgid "" "Could not find a valid value for NXPE. Try a different number of processors." -msgstr "" -"Es konnte keine gültige Anzahl an Prozessoren in X Richtung gefunden werden " -"(NXPE). Versuche es mit einer anderen Zahl an Prozessoren." +msgstr "Es konnte keine gültige Anzahl an Prozessoren in X Richtung gefunden werden (NXPE). Versuche es mit einer anderen Zahl an Prozessoren." #: ../src/sys/options/options_ini.cxx:160 -#, fuzzy, c++-format +#, c++-format msgid "Could not open output file '{:s}'\n" -msgstr "Die Ausgabedatei '{:s}' konnte nicht geöffnet werden\n" +msgstr "" +"Die Ausgabedatei '{:s}' konnte nicht geöffnet werden\n" -#: ../src/bout++.cxx:652 +#: ../src/bout++.cxx:657 #, c++-format msgid "Could not open {:s}/{:s}.{:d} for writing" -msgstr "" +msgstr "Konnte {:s}/{:s}.{:d} nicht zum Schreiben öffnen" #. Error reading -#: ../src/mesh/mesh.cxx:532 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:543 +#, c++-format msgid "Could not read integer array '{:s}'\n" -msgstr "Der Ganzzahlen-Array '{:s}' konnte nicht gelesen werden\n" +msgstr "" +"Der Ganzzahlen-Array '{:s}' konnte nicht gelesen werden\n" #. Failed . Probably not important enough to stop the simulation -#: ../src/bout++.cxx:632 +#: ../src/bout++.cxx:637 msgid "Could not run bout-log-color. Make sure it is in your PATH\n" msgstr "" -"Der Befehl 'bout-log-color' konnte nicht ausgeführt werden. Stellen Sie " -"sicher, dass er sich in $PATH befindet.\n" +"Der Befehl 'bout-log-color' konnte nicht ausgeführt werden. Stellen Sie sicher, dass er sich in $PATH befindet.\n" -#: ../src/solver/solver.cxx:765 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:772 +#, c++-format msgid "Couldn't add Monitor: {:g} is not a multiple of {:g}!" -msgstr "" -"'Monitor' konnte nicht hinzugefügt werden: {:g} ist nicht ein Vielfaches von " -"{:g}!" +msgstr "'Monitor' konnte nicht hinzugefügt werden: {:g} ist nicht ein Vielfaches von {:g}!" -#: ../src/sys/expressionparser.cxx:273 +#: ../src/sys/expressionparser.cxx:312 #, c++-format msgid "" "Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so " @@ -564,179 +632,182 @@ msgid "" "may need to change your input file.\n" "{}" msgstr "" +"Generator '{}' konnte nicht gefunden werden. BOUT++-Ausdrücke unterscheiden nun zwischen Groß- und Kleinschreibung, daher\n" +"müssen Sie möglicherweise Ihre Eingabedatei ändern.\n" +"{}" -#: ../src/mesh/mesh.cxx:568 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:587 +#, c++-format msgid "Couldn't find region {:s} in regionMap2D" msgstr "Die Region '{:s}' ist nicht in regionMap2D" -#: ../src/mesh/mesh.cxx:560 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:571 ../src/mesh/mesh.cxx:579 +#, c++-format msgid "Couldn't find region {:s} in regionMap3D" msgstr "Die Region '{:s}' ist nicht in regionMap3D" -#: ../src/mesh/mesh.cxx:576 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:595 +#, c++-format msgid "Couldn't find region {:s} in regionMapPerp" msgstr "Die Region '{:s}' ist nicht in regionMapPerp" #. Convert any exceptions to something a bit more useful -#: ../src/sys/options.cxx:336 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:361 +#, c++-format msgid "Couldn't get {} from option {:s} = '{:s}': {}" -msgstr "" -"Die Option {:s} = '{:s}' konnte nicht als ganze Zahl interpretiert werden." +msgstr "{} konnte nicht aus der Option {:s} = '{:s}' gelesen werden: {}" -#: ../src/bout++.cxx:508 -#, fuzzy, c++-format +#: ../src/bout++.cxx:515 +#, c++-format msgid "DataDir \"{:s}\" does not exist or is not accessible\n" -msgstr "Der Datenordner \"{:s}\" existiert nicht oder ist nicht lesbar\n" +msgstr "" +"Der Datenordner \"{:s}\" existiert nicht oder ist nicht lesbar\n" -#: ../src/bout++.cxx:505 -#, fuzzy, c++-format +#: ../src/bout++.cxx:512 +#, c++-format msgid "DataDir \"{:s}\" is not a directory\n" msgstr "" "\"{:s}\" soll als Datenordner verwendet werden, ist jedoch kein Ordner\n" -#: ../src/solver/solver.cxx:665 +#: ../src/solver/solver.cxx:671 msgid "ERROR: Solver is already initialised\n" -msgstr "FEHLER: Der Integrator ist bereits initialisiert.\n" +msgstr "" +"FEHLER: Der Integrator ist bereits initialisiert.\n" -#: ../src/bout++.cxx:209 -#, fuzzy, c++-format +#: ../src/bout++.cxx:216 +#, c++-format msgid "Error encountered during initialisation: {:s}\n" -msgstr "Es wurde ein Fehler während der Initialisierung gefunden: {:s}\n" +msgstr "" +"Es wurde ein Fehler während der Initialisierung gefunden: {:s}\n" -#: ../src/bout++.cxx:744 +#: ../src/bout++.cxx:751 msgid "Error whilst writing settings" msgstr "Es wurde ein Fehler beim Schreiben der Einstellungsdatei gefunden" -#: ../src/mesh/impls/bout/boutmesh.cxx:332 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:323 +#, c++-format msgid "Error: nx must be greater than 2 times MXG (2 * {:d})" msgstr "Fehler: nx muss größer als 2 mal MXG sein (2 * {:d})" -#: ../src/solver/solver.cxx:512 +#: ../src/solver/solver.cxx:520 msgid "Failed to initialise solver-> Aborting\n" msgstr "" -"Der Integrator konnte nicht initialisiert werden. Der Prozess wird " -"abgebrochen\n" +"Der Integrator konnte nicht initialisiert werden. Der Prozess wird abgebrochen\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:290 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:281 +#, c++-format msgid "Finding value for NXPE (ideal = {:f})\n" -msgstr "Suche NXPE Wert (optimal = {:f})\n" +msgstr "" +"Suche NXPE Wert (optimal = {:f})\n" -#: ../src/solver/solver.cxx:668 +#: ../src/solver/solver.cxx:674 msgid "Initialising solver\n" -msgstr "initialisiere den Integrator\n" +msgstr "" +"initialisiere den Integrator\n" -#: ../src/bout++.cxx:494 +#: ../src/bout++.cxx:501 msgid "" "Input and output file for settings must be different.\n" "Provide -o to avoid this issue.\n" msgstr "" -"Optionendatei (Eingabe) und Einstellungsdatei (Ausgabe) müssen verschieden " -"sein.\n" +"Optionendatei (Eingabe) und Einstellungsdatei (Ausgabe) müssen verschieden sein.\n" "Verwende -o .\n" #: ../src/sys/optionsreader.cxx:76 msgid "Invalid command line option '-' found - maybe check whitespace?" -msgstr "" +msgstr "Invalid command line option '-' found - maybe check whitespace?" -#: ../src/mesh/impls/bout/boutmesh.cxx:400 +#: ../src/mesh/impls/bout/boutmesh.cxx:436 msgid "Loading mesh" msgstr "Lade das Gitter" -#: ../src/mesh/impls/bout/boutmesh.cxx:415 +#: ../src/mesh/impls/bout/boutmesh.cxx:451 msgid "Mesh must contain nx" msgstr "Das Gitter muss nx enthalten" -#: ../src/mesh/impls/bout/boutmesh.cxx:419 +#: ../src/mesh/impls/bout/boutmesh.cxx:455 msgid "Mesh must contain ny" msgstr "Das Gitter muss ny enthalten" #. Not found -#: ../src/mesh/mesh.cxx:536 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:547 +#, c++-format msgid "Missing integer array {:s}\n" -msgstr "Ganzzahlen-Array '{:s}' nicht gesetzt\n" +msgstr "" +"Ganzzahlen-Array '{:s}' nicht gesetzt\n" -#: ../src/solver/solver.cxx:905 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:911 +#, c++-format msgid "Monitor signalled to quit (exception {})\n" -msgstr "Beendigung durch Monitor\n" +msgstr "" +"Monitor signalisierte Beenden (Ausnahme {})\n" -#: ../src/solver/solver.cxx:883 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:889 +#, c++-format msgid "Monitor signalled to quit (return code {})" -msgstr "Der Monitor signaliserte die Beendigung" +msgstr "Monitor signalisierte Beenden (Rückgabecode {})" -#: ../src/bout++.cxx:823 +#: ../src/bout++.cxx:834 msgid "Name of file whose existence triggers a stop" -msgstr "" +msgstr "Name of file whose existence triggers a stop" -#: ../src/mesh/impls/bout/boutmesh.cxx:565 +#: ../src/mesh/impls/bout/boutmesh.cxx:601 msgid "No boundary regions in this processor" msgstr "Keine Randregionen auf diesem Prozessor" -#: ../src/mesh/impls/bout/boutmesh.cxx:550 -#, fuzzy +#: ../src/mesh/impls/bout/boutmesh.cxx:586 msgid "No boundary regions; domain is periodic\n" -msgstr "Keine Randregionen auf diesem Prozessor" +msgstr "" +"Keine Randregionen; Domäne ist periodisch\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:254 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:245 +#, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n" msgstr "" -"Anzahl an Prozessoren ({:d}) nicht teilbar durch Anzahl in x Richtung " -"({:d})\n" +"Anzahl an Prozessoren ({:d}) nicht teilbar durch Anzahl in x Richtung ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:267 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:258 +#, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n" msgstr "" -"Anzahl an Prozessoren ({:d}) nicht teilbar durch Anzahl in x Richtung " -"({:d})\n" +"Anzahl an Prozessoren ({:d}) nicht teilbar durch Anzahl in x Richtung ({:d})\n" #. Less than 2 time-steps left -#: ../src/bout++.cxx:896 -#, fuzzy, c++-format +#: ../src/bout++.cxx:908 +#, c++-format msgid "Only {:e} seconds ({:.2f} steps) left. Quitting\n" -msgstr "Nur noch {:e} Sekunden verfügbar. Abbruch\n" +msgstr "" +"Nur noch {:e} Sekunden ({:.2f} Schritte) übrig. Beende\n" -#: ../src/sys/options.cxx:303 ../src/sys/options.cxx:345 -#: ../src/sys/options.cxx:393 ../src/sys/options.cxx:428 -#: ../src/sys/options.cxx:703 ../src/sys/options.cxx:730 -#: ../src/sys/options.cxx:757 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:382 ../src/sys/options.cxx:398 +#: ../src/sys/options.cxx:441 ../src/sys/options.cxx:471 +#: ../src/sys/options.cxx:745 ../src/sys/options.cxx:767 +#: ../src/sys/options.cxx:789 +#, c++-format msgid "Option {:s} has no value" msgstr "Der Option '{:s}' wurde kein Wert zugewiesen" #. Doesn't exist -#: ../src/sys/options.cxx:159 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:172 +#, c++-format msgid "Option {:s}:{:s} does not exist" msgstr "Die Option {:s}:{:s} exisitiert nicht" -#: ../include/bout/options.hxx:828 -#, fuzzy, c++-format +#: ../include/bout/options.hxx:904 +#, c++-format msgid "" "Options: Setting a value from same source ({:s}) to new value '{:s}' - old " "value was '{:s}'." -msgstr "" -"Optionen: Der Wert {2:s} wird mit dem Wert {1:s} gleichen Ursprungs ({0:s}) " -"überschrieben." +msgstr "Optionen: Setze einen Wert aus derselben Quelle ({:s}) auf den neuen Wert '{:s}' - der alte Wert war '{:s}'." -#: ../src/mesh/impls/bout/boutmesh.cxx:552 -#, fuzzy +#: ../src/mesh/impls/bout/boutmesh.cxx:588 msgid "Possible boundary regions are: " msgstr "Keine Randregionen auf diesem Prozessor" -#: ../src/bout++.cxx:538 -#, fuzzy, c++-format +#: ../src/bout++.cxx:545 +#, c++-format msgid "" "Processor number: {:d} of {:d}\n" "\n" @@ -744,32 +815,33 @@ msgstr "" "Prozessorennummer: {:d} von {:d}\n" "\n" -#: ../src/mesh/mesh.cxx:609 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:642 +#, c++-format msgid "Registered region 2D {:s}" msgstr "2D Region '{:s}' hinzugefügt" -#: ../src/mesh/mesh.cxx:599 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:632 +#, c++-format msgid "Registered region 3D {:s}" msgstr "3D Region '{:s}' hinzugefügt" -#: ../src/mesh/mesh.cxx:619 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:652 +#, c++-format msgid "Registered region Perp {:s}" msgstr "Perp Region '{:s}' hinzugefügt" -#: ../src/bout++.cxx:529 -#, fuzzy, c++-format +#: ../src/bout++.cxx:536 +#, c++-format msgid "Revision: {:s}\n" -msgstr "Revision: {:s}\n" +msgstr "" +"Revision: {:s}\n" -#: ../src/solver/solver.cxx:581 +#: ../src/solver/solver.cxx:587 msgid "Run time : " msgstr "Dauer: " #. / Run the solver -#: ../src/solver/solver.cxx:525 +#: ../src/solver/solver.cxx:533 msgid "" "Running simulation\n" "\n" @@ -779,77 +851,73 @@ msgstr "" #: ../tests/unit/src/test_bout++.cxx:359 msgid "Signal" -msgstr "" +msgstr "Signal" -#: ../src/bout++.cxx:865 +#: ../src/bout++.cxx:876 msgid "" "Sim Time | RHS evals | Wall Time | Calc Inv Comm I/O SOLVER\n" "\n" msgstr "" -"Simu Zeit | RHS Berech. | Echtdauer | Rechnen Inver Komm I/O " -"Integrator\n" +"Simu Zeit | RHS Berech. | Echtdauer | Rechnen Inver Komm I/O Integrator\n" "\n" -#: ../src/bout++.cxx:868 +#: ../src/bout++.cxx:879 msgid "" "Sim Time | RHS_e evals | RHS_I evals | Wall Time | Calc Inv " "Comm I/O SOLVER\n" "\n" msgstr "" -"Simu Zeit | #expl RHS | #impl RHS | Echtdauer | Rechnen Inv " -"Komm I/O Integrator\n" +"Simu Zeit | #expl RHS | #impl RHS | Echtdauer | Rechnen Inv Komm I/O Integrator\n" "\n" -#: ../src/solver/solver.cxx:506 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:514 +#, c++-format msgid "Solver running for {:d} outputs with monitor timestep of {:e}\n" msgstr "" -"Integriere mit einem `Monitor`-Zeitschritt von {1:e} für {0:d} Aufrufe.\n" +"Solver läuft für {:d} Ausgaben mit einem Monitor-Zeitschritt von {:e}\n" -#: ../src/solver/solver.cxx:502 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:510 +#, c++-format msgid "Solver running for {:d} outputs with output timestep of {:e}\n" -msgstr "Integriere {:d} Zeitschritte von je {:e}\n" +msgstr "" +"Integriere {:d} Zeitschritte von je {:e}\n" -#: ../src/solver/solver.cxx:781 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:788 +#, c++-format msgid "" "Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) after init is " "called!" -msgstr "" -"Der Integrator kann den Zeitschritt nicht von {:g} auf {:g} reduzieren, " -"nachdem er initialisiert wurde!" +msgstr "Der Integrator kann den Zeitschritt nicht von {:g} auf {:g} reduzieren, nachdem er initialisiert wurde!" -#: ../src/solver/solver.cxx:1281 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:1289 +#, c++-format msgid "" "Time derivative at wrong location - Field is at {:s}, derivative is at {:s} " "for field '{:s}'\n" msgstr "" -"Die zeitliche Ableitung ist an der falschen Stelle. Das Feld '{2:s}' ist an " -"Position {0:s}, während die Ableitung an Position {1:s} ist.\n" +"Zeitliche Ableitung an falscher Stelle - Feld ist bei {:s}, Ableitung ist bei {:s} für Feld '{:s}'\n" -#: ../src/solver/solver.cxx:1480 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:1494 +#, c++-format msgid "Time derivative for variable '{:s}' not set" msgstr "Zeitliche Ableitung für Variable '{:s}' nicht gesetzt" -#: ../src/mesh/mesh.cxx:605 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:638 +#, c++-format msgid "Trying to add an already existing region {:s} to regionMap2D" msgstr "Die Region '{:s}' ist bereits vorhanden in der regionMap2D" -#: ../src/mesh/mesh.cxx:595 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:614 +#, c++-format msgid "Trying to add an already existing region {:s} to regionMap3D" msgstr "Die Region '{:s}' ist bereits vorhanden in der regionMap3D" -#: ../src/mesh/mesh.cxx:616 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:649 +#, c++-format msgid "Trying to add an already existing region {:s} to regionMapPerp" msgstr "Die Region '{:s}' ist bereits vorhanden in der regionMapPerp" -#: ../src/sys/options.cxx:99 ../src/sys/options.cxx:138 +#: ../src/sys/options.cxx:112 ../src/sys/options.cxx:151 #, c++-format msgid "" "Trying to index Option '{0}' with '{1}', but '{0}' is a value, not a " @@ -857,8 +925,10 @@ msgid "" "This is likely the result of clashing input options, and you may have to " "rename one of them.\n" msgstr "" +"Es wird versucht, Option '{0}' mit '{1}' zu indizieren, aber '{0}' ist ein Wert und kein Abschnitt.\n" +"Dies ist wahrscheinlich das Ergebnis kollidierender Eingabeoptionen, und Sie müssen möglicherweise eine davon umbenennen.\n" -#: ../src/mesh/coordinates.cxx:1462 +#: ../src/mesh/coordinates.cxx:1464 msgid "" "Unrecognised paralleltransform option.\n" "Valid choices are 'identity', 'shifted', 'fci'" @@ -866,155 +936,177 @@ msgstr "" "Unbekannte Paralleltransformation\n" "Gültige Optionen sind 'identity', 'shifted', 'fci'" -#: ../src/sys/options.cxx:872 +#: ../src/sys/options.cxx:899 msgid "Unused options:\n" -msgstr "Ungenutzte Optionen:\n" +msgstr "" +"Ungenutzte Optionen:\n" -#: ../src/bout++.cxx:439 -#, fuzzy, c++-format +#: ../src/bout++.cxx:446 +#, c++-format msgid "Usage is {:s} -d \n" -msgstr "Benutzung: {:s} -d \n" +msgstr "" +"Benutzung: {:s} -d \n" -#: ../src/bout++.cxx:448 -#, fuzzy, c++-format +#: ../src/bout++.cxx:455 +#, c++-format msgid "Usage is {:s} -f \n" -msgstr "Benutzung: {:s} -f \n" +msgstr "" +"Benutzung: {:s} -f \n" -#: ../src/bout++.cxx:466 -#, fuzzy, c++-format +#: ../src/bout++.cxx:473 +#, c++-format msgid "Usage is {:s} -l \n" -msgstr "Benutzung: {:s} -f \n" +msgstr "" +"Benutzung: {:s} -f \n" -#: ../src/bout++.cxx:457 -#, fuzzy, c++-format +#: ../src/bout++.cxx:464 +#, c++-format msgid "Usage is {:s} -o \n" -msgstr "Benutzung: {:s} -f \n" +msgstr "" +"Benutzung: {:s} -f \n" -#: ../src/bout++.cxx:353 -#, fuzzy, c++-format +#: ../src/bout++.cxx:360 +#, c++-format msgid "Usage is {} {} \n" -msgstr "Benutzung: {:s} -f \n" +msgstr "" +"Verwendung: {} {} \n" #: ../tests/unit/src/test_bout++.cxx:32 ../tests/unit/src/test_bout++.cxx:46 msgid "Usage:" -msgstr "" +msgstr "Usage:" #. Print help message -- note this will be displayed once per processor as we've not #. started MPI yet. -#: ../src/bout++.cxx:367 -#, fuzzy, c++-format +#: ../src/bout++.cxx:374 +#, c++-format msgid "" "Usage: {:s} [-d ] [-f ] [restart [append]] " "[VAR=VALUE]\n" msgstr "" -"Benutzung: {:s} [-d ] [-f ] [restart [append]] " -"[VAR=WERT]\n" +"Benutzung: {:s} [-d ] [-f ] [restart [append]] [VAR=WERT]\n" #. restart file should be written by physics model -#: ../src/solver/solver.cxx:921 -#, fuzzy +#: ../src/solver/solver.cxx:927 msgid "User signalled to quit. Returning\n" -msgstr "Beendigung durch Monitor\n" +msgstr "" +"Beendigung durch Monitor\n" -#: ../src/sys/options.cxx:373 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:486 +#, c++-format +msgid "Value for option {:s} = {:e} is not a bool" +msgstr "Wert der Option {:s} = {:e} ist keine Ganzzahl" + +#: ../src/sys/options.cxx:426 +#, c++-format msgid "Value for option {:s} = {:e} is not an integer" msgstr "Wert der Option {:s} = {:e} ist keine Ganzzahl" -#: ../src/sys/options.cxx:408 +#: ../src/sys/options.cxx:456 #, c++-format msgid "Value for option {:s} cannot be converted to a BoutReal" -msgstr "" +msgstr "Wert für Option {:s} kann nicht in ein BoutReal umgewandelt werden" -#: ../src/sys/options.cxx:581 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:623 +#, c++-format msgid "Value for option {:s} cannot be converted to a Field2D" -msgstr "Wert der Option {:s} = {:e} ist keine Ganzzahl" +msgstr "Wert für Option {:s} kann nicht in ein Field2D umgewandelt werden" -#: ../src/sys/options.cxx:529 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:571 +#, c++-format msgid "Value for option {:s} cannot be converted to a Field3D" -msgstr "Wert der Option {:s} = {:e} ist keine Ganzzahl" +msgstr "Wert für Option {:s} kann nicht in ein Field3D umgewandelt werden" -#: ../src/sys/options.cxx:663 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:705 +#, c++-format msgid "Value for option {:s} cannot be converted to a FieldPerp" -msgstr "Wert der Option {:s} = {:e} ist keine Ganzzahl" +msgstr "Wert für Option {:s} kann nicht in ein FieldPerp umgewandelt werden" -#: ../src/sys/options.cxx:451 +#: ../src/sys/options.cxx:491 #, c++-format msgid "Value for option {:s} cannot be converted to a bool" -msgstr "" +msgstr "Wert für Option {:s} kann nicht in ein bool umgewandelt werden" -#: ../src/sys/options.cxx:709 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:751 +#, c++-format msgid "Value for option {:s} cannot be converted to an Array" -msgstr "Wert der Option {:s} = {:e} ist keine Ganzzahl" +msgstr "Wert für Option {:s} kann nicht in ein Array umgewandelt werden" -#: ../src/sys/options.cxx:736 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:773 +#, c++-format msgid "Value for option {:s} cannot be converted to an Matrix" -msgstr "Wert der Option {:s} = {:e} ist keine Ganzzahl" +msgstr "Wert für Option {:s} kann nicht in eine Matrix umgewandelt werden" -#: ../src/sys/options.cxx:763 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:795 +#, c++-format msgid "Value for option {:s} cannot be converted to an Tensor" -msgstr "Wert der Option {:s} = {:e} ist keine Ganzzahl" +msgstr "Wert für Option {:s} kann nicht in einen Tensor umgewandelt werden" #. Another type which can't be converted -#: ../src/sys/options.cxx:365 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:418 +#, c++-format msgid "Value for option {:s} is not an integer" -msgstr "Wert der Option {:s} = {:e} ist keine Ganzzahl" +msgstr "Wert für Option {:s} ist keine Ganzzahl" -#: ../src/solver/solver.cxx:1232 ../src/solver/solver.cxx:1238 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:1240 ../src/solver/solver.cxx:1246 +#, c++-format msgid "Variable '{:s}' not initialised" msgstr "Variable '{:s}' ist nicht initialisiert" -#: ../src/mesh/impls/bout/boutmesh.cxx:431 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:467 +#, c++-format msgid "" "WARNING: Number of toroidal points should be 2^n for efficient FFT " "performance -- consider changing MZ ({:d}) if using FFTs\n" msgstr "" -"WARNUNG: Anzahl der toroidalen Punkte sollte 2^n für effiziente FFTs sein. " -"Ändere MZ falls FFTs verwendet werden\n" +"WARNUNG: Die Anzahl toroidaler Punkte sollte für effiziente FFT-Leistung 2^n sein -- ziehen Sie eine Änderung von MZ ({:d}) in Betracht, wenn Sie FFTs verwenden\n" -#: ../src/mesh/coordinates.cxx:633 +#: ../src/mesh/coordinates.cxx:635 msgid "WARNING: extrapolating input mesh quantities into x-boundary cells\n" msgstr "" +"WARNING: extrapolating input mesh quantities into x-boundary cells\n" -#: ../src/mesh/coordinates.cxx:410 +#: ../src/mesh/coordinates.cxx:412 msgid "" "WARNING: extrapolating input mesh quantities into x-boundary cells. Set " "option extrapolate_x=false to disable this.\n" msgstr "" +"WARNING: extrapolating input mesh quantities into x-boundary cells. Set option extrapolate_x=false to disable this.\n" -#: ../src/mesh/coordinates.cxx:638 +#: ../src/mesh/coordinates.cxx:640 msgid "WARNING: extrapolating input mesh quantities into y-boundary cells\n" msgstr "" +"WARNING: extrapolating input mesh quantities into y-boundary cells\n" -#: ../src/mesh/coordinates.cxx:415 +#: ../src/mesh/coordinates.cxx:417 msgid "" "WARNING: extrapolating input mesh quantities into y-boundary cells. Set " "option extrapolate_y=false to disable this.\n" msgstr "" +"WARNING: extrapolating input mesh quantities into y-boundary cells. Set option extrapolate_y=false to disable this.\n" -#: ../src/bout++.cxx:814 +#: ../src/bout++.cxx:825 msgid "Wall time limit in hours. By default (< 0), no limit" -msgstr "" +msgstr "Wall time limit in hours. By default (< 0), no limit" #: ../src/sys/optionsreader.cxx:42 -#, fuzzy, c++-format +#, c++-format msgid "Writing options to file {:s}\n" -msgstr "Optionen werden in {:s} gespeichert\n" +msgstr "" +"Optionen werden in {:s} gespeichert\n" #. / The source label given to default values -#: ../src/sys/options.cxx:15 +#: ../src/sys/options.cxx:34 msgid "default" msgstr "Vorgabe" +#, fuzzy, c++-format +#~ msgid "\tOpenMP parallelisation {}" +#~ msgstr "\tOpenMP Parallelisierung ist deaktiviert\n" + +#, fuzzy, c++-format +#~ msgid "\tOption '{:s}': Boolean expected. Got '{:s}'\n" +#~ msgstr "\tOption '{:s}': Boolscherwert erwartet, '{:s}' gefunden\n" + #~ msgid "\tChecking disabled\n" #~ msgstr "\tChecks sind deaktiviert\n" @@ -1022,10 +1114,6 @@ msgstr "Vorgabe" #~ msgid "\tChecking enabled, level {:d}\n" #~ msgstr "\tChecks der Stufe {:d} sind aktiviert\n" -#, fuzzy -#~ msgid "\tOpenMP parallelisation enabled, using {:d} threads\n" -#~ msgstr "\tOpenMP Parallelisierung mit {:d} Threads ist aktiviert\n" - #~ msgid "\tSignal handling enabled\n" #~ msgstr "\tSignalverarbeitung ist aktiviert\n" @@ -1051,8 +1139,8 @@ msgstr "Vorgabe" #~ "überschrieben. Benötigt `restart`\n" #~ " VAR=WERT \t\tSetzt den Wert WERT für die Variable VAR\n" #~ "\n" -#~ "Weitere Eingabeparameter sind in dem Manual und dem Quellcode (z.B. {:s}." -#~ "cxx) des Physikmoduls definiert.\n" +#~ "Weitere Eingabeparameter sind in dem Manual und dem Quellcode (z.B. " +#~ "{:s}.cxx) des Physikmoduls definiert.\n" #, fuzzy #~ msgid "Couldn't get BoutReal from option {:s} = '{:s}'" diff --git a/locale/es/libbout.po b/locale/es/libbout.po index 8a3b22be61..3346bd882e 100644 --- a/locale/es/libbout.po +++ b/locale/es/libbout.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: BOUT++ 4.2.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-12 09:17+0100\n" +"POT-Creation-Date: 2025-08-13 23:37+0100\n" "PO-Revision-Date: 2019-02-11 12:46+0900\n" "Last-Translator: Marta \n" "Language-Team: Spanish\n" @@ -16,132 +16,134 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:191 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:182 +#, c++-format msgid "" "\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" -"\t -> La región `Core` jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) debe ser un " -"múltiplo de MYSUB ({:d})\n" +"\t -> La región `Core` jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) debe ser un múltiplo de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:224 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:215 +#, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" -"\t -> La región `Core` jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) debe ser un " -"múltiplo de MYSUB ({:d})\n" +"\t -> La región `Core` jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) debe ser un múltiplo de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:199 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:190 +#, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" -"\t -> La región `Core` jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) debe ser un " -"múltiplo de MYSUB ({:d})\n" +"\t -> La región `Core` jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) debe ser un múltiplo de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:309 +#: ../src/mesh/impls/bout/boutmesh.cxx:300 msgid "\t -> Good value\n" -msgstr "\t -> El valor es bueno\n" +msgstr "" +"\t -> El valor es bueno\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:180 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:171 +#, c++-format msgid "" "\t -> Leg region jyseps1_1+1 ({:d}) must be a multiple of MYSUB ({:d})\n" msgstr "" -"\t -> La región `Leg` jyseps1_1+1 ({:d}) debe ser un múltiplo de MYSUB " -"({:d})\n" +"\t -> La región `Leg` jyseps1_1+1 ({:d}) debe ser un múltiplo de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:215 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:206 +#, c++-format msgid "" "\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" -"\t -> La región `Leg` jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) debe ser un " -"múltiplo de MYSUB ({:d})\n" +"\t -> La región `Leg` jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) debe ser un múltiplo de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:232 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:223 +#, c++-format msgid "" "\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a multiple of " "MYSUB ({:d})\n" msgstr "" -"\t -> La región `Leg` ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) debe ser un " -"múltiplo de MYSUB ({:d})\n" +"\t -> La región `Leg` ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) debe ser un múltiplo de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:208 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:199 +#, c++-format msgid "" "\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" -"\t -> La región `Leg` ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) debe ser un " -"múltiplo de MYSUB ({:d})\n" +"\t -> La región `Leg` ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) debe ser un múltiplo de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:175 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:166 +#, c++-format msgid "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n" -msgstr "\t -> ny/NYPE ({:d}/{:d} = {:d}) debe ser >= MYG ({:d})\n" +msgstr "" +"\t -> ny/NYPE ({:d}/{:d} = {:d}) debe ser >= MYG ({:d})\n" #: ../src/bout++.cxx:575 #, c++-format +msgid "\tADIOS2 support {}\n" +msgstr "" +"\tCompatibilidad con ADIOS2 {}\n" + +#: ../src/bout++.cxx:583 +#, c++-format msgid "\tBacktrace in exceptions {}\n" msgstr "" +"\tTraza inversa en excepciones {}\n" #. Loop over all possibilities #. Processors divide equally #. Mesh in X divides equally #. Mesh in Y divides equally -#: ../src/mesh/impls/bout/boutmesh.cxx:297 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:288 +#, c++-format msgid "\tCandidate value: {:d}\n" -msgstr "\tValor candidato: {:d}\n" +msgstr "" +"\tValor candidato: {:d}\n" -#: ../src/bout++.cxx:576 +#: ../src/bout++.cxx:584 #, c++-format msgid "\tColour in logs {}\n" msgstr "" +"\tColor en los registros {}\n" -#: ../src/bout++.cxx:594 +#: ../src/bout++.cxx:599 msgid "\tCommand line options for this run : " msgstr "\tParámetros de línea de comandos para esta ejecución :" #. The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings #. which could cause problems (e.g. terminate strings). -#: ../src/bout++.cxx:590 -#, fuzzy, c++-format +#: ../src/bout++.cxx:595 +#, c++-format msgid "\tCompiled with flags : {:s}\n" -msgstr "\tCompilado con las opciones `flags` : {:s}\n" +msgstr "" +"\tCompilado con las opciones `flags` : {:s}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:324 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:315 +#, c++-format msgid "" "\tDomain split (NXPE={:d}, NYPE={:d}) into domains (localNx={:d}, " "localNy={:d})\n" msgstr "" -"\tDominio separado (NXPE={:d}, NYPE={:d}) en los dominios (localNx={:d}, " -"localNy={:d})\n" +"\tDominio separado (NXPE={:d}, NYPE={:d}) en los dominios (localNx={:d}, localNy={:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:364 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:357 +#, c++-format msgid "\tERROR: Cannot split {:d} Y points equally between {:d} processors\n" msgstr "" -"\tERROR: No se pueden separar {:d} Y puntos entre {:d} procesadores por " -"igual\n" +"\tERROR: No se pueden separar {:d} Y puntos entre {:d} procesadores por igual\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:372 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:365 +#, c++-format msgid "\tERROR: Cannot split {:d} Z points equally between {:d} processors\n" msgstr "" -"\tERROR: No se pueden separar {:d} Y puntos entre {:d} procesadores por " -"igual\n" +"\tERROR: No se pueden separar {:d} Y puntos entre {:d} procesadores por igual\n" #: ../src/sys/options/options_ini.cxx:200 -#, fuzzy, c++-format +#, c++-format msgid "" "\tEmpty key\n" "\tLine: {:s}" @@ -150,41 +152,45 @@ msgstr "" "\tLínea: {:s}" #: ../src/sys/optionsreader.cxx:127 -#, fuzzy, c++-format +#, c++-format msgid "\tEmpty key or value in command line '{:s}'\n" -msgstr "\tEntrada o valor vacío en la línea de comandos '{:s}'\n" +msgstr "" +"\tEntrada o valor vacío en la línea de comandos '{:s}'\n" -#: ../src/bout++.cxx:582 +#: ../src/bout++.cxx:587 #, c++-format msgid "\tExtra debug output {}\n" msgstr "" +"\tSalida adicional de depuración {}\n" -#: ../src/bout++.cxx:561 -#, fuzzy, c++-format +#: ../src/bout++.cxx:568 +#, c++-format msgid "\tFFT support {}\n" -msgstr "\tSoporte netCDF activado\n" +msgstr "" +"\tCompatibilidad con FFT {}\n" -#: ../src/bout++.cxx:585 +#: ../src/bout++.cxx:590 #, c++-format msgid "\tField name tracking {}\n" msgstr "" +"\tSeguimiento del nombre de campo {}\n" -#: ../src/bout++.cxx:583 +#: ../src/bout++.cxx:588 #, c++-format msgid "\tFloating-point exceptions {}\n" msgstr "" +"\tExcepciones de punto flotante {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:440 +#: ../src/mesh/impls/bout/boutmesh.cxx:476 msgid "\tGrid size: " msgstr "\tTamaño de la malla (`grid`): " -#: ../src/mesh/impls/bout/boutmesh.cxx:463 -#, fuzzy +#: ../src/mesh/impls/bout/boutmesh.cxx:499 msgid "\tGuard cells (x,y,z): " -msgstr "\tProteger celdas (x,y): " +msgstr "\tCeldas de guarda (x,y,z): " #: ../src/sys/options/options_ini.cxx:204 -#, fuzzy, c++-format +#, c++-format msgid "" "\tKey must not contain ':' character\n" "\tLine: {:s}" @@ -192,139 +198,156 @@ msgstr "" "\tLa entrada no debe contener el carácter ':'\n" "\tLínea: {:s}" -#: ../src/bout++.cxx:563 +#: ../src/bout++.cxx:570 #, c++-format msgid "\tLAPACK support {}\n" msgstr "" +"\tCompatibilidad con LAPACK {}\n" -#: ../src/bout++.cxx:586 +#: ../src/bout++.cxx:591 #, c++-format msgid "\tMessage stack {}\n" msgstr "" +"\tPila de mensajes {}\n" -#: ../src/bout++.cxx:560 +#: ../src/bout++.cxx:567 #, c++-format msgid "\tMetrics mode is {}\n" msgstr "" +"\tEl modo de métricas es {}\n" #: ../src/sys/optionsreader.cxx:111 -#, fuzzy, c++-format +#, c++-format msgid "\tMultiple '=' in command-line argument '{:s}'\n" -msgstr "\tMutilples '=' en el argumento de la línea de comandos '{:s}'\n" +msgstr "" +"\tMutilples '=' en el argumento de la línea de comandos '{:s}'\n" -#: ../src/bout++.cxx:562 +#: ../src/bout++.cxx:569 #, c++-format msgid "\tNatural language support {}\n" msgstr "" +"\tCompatibilidad con lenguaje natural {}\n" -#: ../src/bout++.cxx:567 -#, fuzzy, c++-format +#: ../src/bout++.cxx:574 +#, c++-format msgid "\tNetCDF support {}{}\n" -msgstr "\tSoporte netCDF activado\n" +msgstr "" +"\tCompatibilidad con NetCDF {}{}\n" -#: ../src/bout++.cxx:577 -#, fuzzy, c++-format -msgid "\tOpenMP parallelisation {}" -msgstr "\tParalelización en OpenMP desactivada\n" +#: ../src/bout++.cxx:585 +#, c++-format +msgid "\tOpenMP parallelisation {}, using {} threads\n" +msgstr "" +"\tParalelización con OpenMP {}, usando {} hilos\n" #. Mark the option as used #. Option not found -#: ../src/sys/options.cxx:311 ../src/sys/options.cxx:380 -#: ../src/sys/options.cxx:415 ../src/sys/options.cxx:457 -#: ../src/sys/options.cxx:717 ../src/sys/options.cxx:744 -#: ../src/sys/options.cxx:771 ../include/bout/options.hxx:516 -#: ../include/bout/options.hxx:549 ../include/bout/options.hxx:573 -#: ../include/bout/options.hxx:820 +#: ../include/bout/options.hxx:586 ../include/bout/options.hxx:619 +#: ../include/bout/options.hxx:643 ../include/bout/options.hxx:896 msgid "\tOption " msgstr "\tOpción " -#: ../src/sys/options.cxx:447 -#, fuzzy, c++-format -msgid "\tOption '{:s}': Boolean expected. Got '{:s}'\n" -msgstr "\tOpción '{:s}': valor Booleano esperado. Se obtuvo '{:s}'\n" +#: ../src/sys/options.cxx:369 +#, c++-format +msgid "\tOption {} = {}" +msgstr "\tOpción {} = {}" #: ../src/sys/options/options_ini.cxx:70 -#, fuzzy, c++-format +#, c++-format msgid "\tOptions file '{:s}' not found\n" -msgstr "\tOpciones de archivo '{:s}' no encontrados\n" +msgstr "" +"\tOpciones de archivo '{:s}' no encontrados\n" -#: ../src/bout++.cxx:568 +#: ../src/bout++.cxx:576 #, c++-format msgid "\tPETSc support {}\n" msgstr "" +"\tCompatibilidad con PETSc {}\n" -#: ../src/bout++.cxx:571 +#: ../src/bout++.cxx:579 #, c++-format msgid "\tPVODE support {}\n" msgstr "" +"\tCompatibilidad con PVODE {}\n" -#: ../src/bout++.cxx:557 +#: ../src/bout++.cxx:564 msgid "\tParallel NetCDF support disabled\n" -msgstr "\tSoporte para NetCDF paralelo desactivado\n" +msgstr "" +"\tSoporte para NetCDF paralelo desactivado\n" -#: ../src/bout++.cxx:555 +#: ../src/bout++.cxx:562 msgid "\tParallel NetCDF support enabled\n" -msgstr "\tSoporte para NetCDF paralelo activado\n" +msgstr "" +"\tSoporte para NetCDF paralelo activado\n" -#: ../src/bout++.cxx:569 +#: ../src/bout++.cxx:577 #, c++-format msgid "\tPretty function name support {}\n" msgstr "" +"\tCompatibilidad con nombres de función detallados {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:437 +#: ../src/mesh/impls/bout/boutmesh.cxx:473 msgid "\tRead nz from input grid file\n" -msgstr "\tLeer nz del archivo input de la malla `grid`\n" +msgstr "" +"\tLeer nz del archivo input de la malla `grid`\n" -#: ../src/mesh/mesh.cxx:238 +#: ../src/mesh/mesh.cxx:249 msgid "\tReading contravariant vector " msgstr "\tLeyendo vector contravariante " -#: ../src/mesh/mesh.cxx:231 ../src/mesh/mesh.cxx:252 +#: ../src/mesh/mesh.cxx:242 ../src/mesh/mesh.cxx:263 msgid "\tReading covariant vector " msgstr "\tLeyendo vector covariante " -#: ../src/bout++.cxx:548 +#: ../src/bout++.cxx:555 #, c++-format msgid "\tRuntime error checking {}" -msgstr "" +msgstr "\tComprobación de errores en tiempo de ejecución {}" -#: ../src/bout++.cxx:573 +#: ../src/bout++.cxx:581 #, c++-format msgid "\tSLEPc support {}\n" msgstr "" +"\tCompatibilidad con SLEPc {}\n" -#: ../src/bout++.cxx:574 +#: ../src/bout++.cxx:582 #, c++-format msgid "\tSUNDIALS support {}\n" msgstr "" +"\tCompatibilidad con SUNDIALS {}\n" -#: ../src/bout++.cxx:572 +#: ../src/bout++.cxx:580 #, c++-format msgid "\tScore-P support {}\n" msgstr "" +"\tCompatibilidad con Score-P {}\n" -#: ../src/bout++.cxx:584 -#, fuzzy, c++-format +#: ../src/bout++.cxx:589 +#, c++-format msgid "\tSignal handling support {}\n" -msgstr "\tGestión de señal desactivada\n" +msgstr "" +"\tCompatibilidad con manejo de señales {}\n" #: ../src/solver/impls/split-rk/split-rk.cxx:76 #, c++-format msgid "\tUsing a timestep {:e}\n" msgstr "" +"\tUsando un paso de tiempo {:e}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:577 +#: ../src/mesh/impls/bout/boutmesh.cxx:613 msgid "\tdone\n" -msgstr "\tlisto\n" +msgstr "" +"\tlisto\n" #: ../src/solver/impls/split-rk/split-rk.cxx:41 msgid "" "\n" "\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n" msgstr "" +"\n" +"\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n" -#: ../src/bout++.cxx:371 -#, fuzzy +#: ../src/bout++.cxx:378 msgid "" "\n" " -d \t\tLook in for input/output files\n" @@ -337,22 +360,24 @@ msgstr "" "\n" " -d \tBuscar en los archivos input/output\n" " -f \tUsar OPCIONES descritas en \n" -" -o \tGuardar OPCIONES usadas descritas en \n" +" -o \tGuardar OPCIONES usadas descritas en \n" " -l, --log \tImprimir registro `log` en \n" " -v, --verbose\t\tAumentar verbosidad\n" " -q, --quiet\t\tDisminuir verbosidad\n" -#: ../src/sys/expressionparser.cxx:302 +#: ../src/sys/expressionparser.cxx:341 #, c++-format msgid "" "\n" " {1: ^{2}}{0}\n" " Did you mean '{0}'?" msgstr "" +"\n" +" {1: ^{2}}{0}\n" +" ¿Quiso decir '{0}'?" -#: ../src/solver/solver.cxx:580 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:586 +#, c++-format msgid "" "\n" "Run finished at : {:s}\n" @@ -360,8 +385,8 @@ msgstr "" "\n" "Ejecución finalizada en : {:s}\n" -#: ../src/solver/solver.cxx:532 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:540 +#, c++-format msgid "" "\n" "Run started at : {:s}\n" @@ -372,7 +397,7 @@ msgstr "" #. Raw string to help with the formatting of the message, and a #. separate variable so clang-format doesn't barf on the #. exception -#: ../src/sys/options.cxx:1102 +#: ../src/sys/options.cxx:1158 msgid "" "\n" "There were unused input options:\n" @@ -399,8 +424,32 @@ msgid "" "\n" "{}" msgstr "" +"\n" +"There were unused input options:\n" +"-----\n" +"{:i}\n" +"-----\n" +"It's possible you've mistyped some options. BOUT++ input arguments are\n" +"now case-sensitive, and some have changed name. You can try running\n" +"\n" +" /bin/bout-v5-input-file-upgrader.py {}/{}\n" +"\n" +"to automatically fix the most common issues. If these options above\n" +"are sometimes used depending on other options, you can call\n" +"`Options::setConditionallyUsed()`, for example:\n" +"\n" +" Options::root()[\"{}\"].setConditionallyUsed();\n" +"\n" +"to mark a section or value as depending on other values, and so ignore\n" +"it in this check. Alternatively, if you're sure the above inputs are\n" +"not a mistake, you can set 'input:error_on_unused_options=false' to\n" +"turn off this check for unused options. You can always set\n" +"'input:validate=true' to check inputs without running the full\n" +"simulation.\n" +"\n" +"{}" -#: ../src/bout++.cxx:382 +#: ../src/bout++.cxx:389 #, c++-format msgid "" " --print-config\t\tPrint the compile-time configuration\n" @@ -432,59 +481,84 @@ msgid "" "For all possible input parameters, see the user manual and/or the physics " "model source (e.g. {:s}.cxx)\n" msgstr "" +" --print-config\t\tPrint the compile-time configuration\n" +" --list-solvers\t\tList the available time solvers\n" +" --help-solver \tPrint help for the given time solver\n" +" --list-laplacians\t\tList the available Laplacian inversion solvers\n" +" --help-laplacian \tPrint help for the given Laplacian inversion solver\n" +" --list-laplacexz\t\tList the available LaplaceXZ inversion solvers\n" +" --help-laplacexz \tPrint help for the given LaplaceXZ inversion solver\n" +" --list-invertpars\t\tList the available InvertPar solvers\n" +" --help-invertpar \tPrint help for the given InvertPar solver\n" +" --list-rkschemes\t\tList the available Runge-Kutta schemes\n" +" --help-rkscheme \tPrint help for the given Runge-Kutta scheme\n" +" --list-meshes\t\t\tList the available Meshes\n" +" --help-mesh \t\tPrint help for the given Mesh\n" +" --list-xzinterpolations\tList the available XZInterpolations\n" +" --help-xzinterpolation \tPrint help for the given XZInterpolation\n" +" --list-zinterpolations\tList the available ZInterpolations\n" +" --help-zinterpolation \tPrint help for the given ZInterpolation\n" +" -h, --help\t\t\tThis message\n" +" restart [append]\t\tRestart the simulation. If append is specified, append to the existing output files, otherwise overwrite them\n" +" VAR=VALUE\t\t\tSpecify a VALUE for input parameter VAR\n" +"\n" +"For all possible input parameters, see the user manual and/or the physics model source (e.g. {:s}.cxx)\n" -#: ../src/bout++.cxx:379 -#, fuzzy +#: ../src/bout++.cxx:386 msgid " -c, --color\t\t\tColor output using bout-log-color\n" -msgstr " -c, --color\t\tSalida de color usando bout-log-color\n" +msgstr "" +" -c, --color\t\tSalida de color usando bout-log-color\n" -#: ../include/bout/options.hxx:823 +#: ../include/bout/options.hxx:899 msgid ") overwritten with:" -msgstr "" +msgstr ") sobrescrito con:" -#: ../src/bout++.cxx:550 +#: ../src/bout++.cxx:557 #, c++-format msgid ", level {}" -msgstr "" - -#: ../src/bout++.cxx:579 -#, c++-format -msgid ", using {} threads" -msgstr "" +msgstr ", level {}" #: ../tests/unit/src/test_bout++.cxx:352 msgid "4 of 8" -msgstr "" +msgstr "4 of 8" -#: ../src/sys/options.cxx:868 +#: ../src/sys/options.cxx:895 msgid "All options used\n" -msgstr "Usando todas las opciones\n" +msgstr "" +"Usando todas las opciones\n" -#: ../src/bout++.cxx:528 -#, fuzzy, c++-format +#: ../src/bout++.cxx:535 +#, c++-format msgid "BOUT++ version {:s}\n" -msgstr "Versión de BOUT++ {:s}\n" +msgstr "" +"Versión de BOUT++ {:s}\n" -#: ../src/bout++.cxx:143 -#, fuzzy +#: ../src/bout++.cxx:147 msgid "Bad command line arguments:\n" -msgstr "\tMutilples '=' en el argumento de la línea de comandos '{:s}'\n" +msgstr "" +"Bad command line arguments:\n" + +#: ../src/sys/expressionparser.cxx:192 +#, c++-format +msgid "Boolean operator argument {:e} is not a bool" +msgstr "Boolean operator argument {:e} is not a bool" -#: ../src/mesh/impls/bout/boutmesh.cxx:559 +#: ../src/mesh/impls/bout/boutmesh.cxx:595 msgid "Boundary regions in this processor: " msgstr "Regiones frontera en este procesador: " -#: ../src/mesh/impls/bout/boutmesh.cxx:355 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:348 +#, c++-format msgid "Cannot split {:d} X points equally between {:d} processors\n" -msgstr "No se pueden dividir {:d} X points entre {:d} procesadores por igual\n" +msgstr "" +"No se pueden dividir {:d} X points entre {:d} procesadores por igual\n" -#: ../src/bout++.cxx:818 +#: ../src/bout++.cxx:829 msgid "Check if a file exists, and exit if it does." -msgstr "" +msgstr "Check if a file exists, and exit if it does." -#: ../src/bout++.cxx:533 -#, fuzzy, c++-format +#: ../src/bout++.cxx:540 +#, c++-format msgid "" "Code compiled on {:s} at {:s}\n" "\n" @@ -496,60 +570,59 @@ msgstr "" msgid "Command line" msgstr "Línea de comandos" -#: ../src/bout++.cxx:544 ../tests/unit/src/test_bout++.cxx:358 +#: ../src/bout++.cxx:551 ../tests/unit/src/test_bout++.cxx:358 msgid "Compile-time options:\n" -msgstr "Opciones de tiempo de compilación:\n" +msgstr "" +"Opciones de tiempo de compilación:\n" #: ../tests/unit/src/test_bout++.cxx:362 -#, fuzzy msgid "Compiled with flags" -msgstr "\tCompilado con las opciones `flags` : {:s}\n" +msgstr "Compiled with flags" -#: ../src/mesh/impls/bout/boutmesh.cxx:568 +#: ../src/mesh/impls/bout/boutmesh.cxx:604 msgid "Constructing default regions" msgstr "Construyendo regiones por defecto" -#: ../src/bout++.cxx:520 -#, fuzzy, c++-format +#: ../src/bout++.cxx:527 +#, c++-format msgid "Could not create PID file {:s}" -msgstr "No se pudo abrir el archivo de salida `output` '{:s}'\n" +msgstr "No se pudo crear el archivo PID {:s}" -#: ../src/mesh/impls/bout/boutmesh.cxx:318 +#: ../src/mesh/impls/bout/boutmesh.cxx:309 msgid "" "Could not find a valid value for NXPE. Try a different number of processors." -msgstr "" -"No se pudo encontrar un valor válido para NXPE. Intente usar un número " -"diferente de procesadores." +msgstr "No se pudo encontrar un valor válido para NXPE. Intente usar un número diferente de procesadores." #: ../src/sys/options/options_ini.cxx:160 -#, fuzzy, c++-format +#, c++-format msgid "Could not open output file '{:s}'\n" -msgstr "No se pudo abrir el archivo de salida `output` '{:s}'\n" +msgstr "" +"No se pudo abrir el archivo de salida `output` '{:s}'\n" -#: ../src/bout++.cxx:652 +#: ../src/bout++.cxx:657 #, c++-format msgid "Could not open {:s}/{:s}.{:d} for writing" -msgstr "" +msgstr "No se pudo abrir {:s}/{:s}.{:d} para escritura" #. Error reading -#: ../src/mesh/mesh.cxx:532 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:543 +#, c++-format msgid "Could not read integer array '{:s}'\n" -msgstr "No se pudo leer la matriz de enteros '{:s}'\n" +msgstr "" +"No se pudo leer la matriz de enteros '{:s}'\n" #. Failed . Probably not important enough to stop the simulation -#: ../src/bout++.cxx:632 +#: ../src/bout++.cxx:637 msgid "Could not run bout-log-color. Make sure it is in your PATH\n" msgstr "" -"No se pudo ejecutar bout-log-color. Asegúrese de que se encuentre en su " -"PATH\n" +"No se pudo ejecutar bout-log-color. Asegúrese de que se encuentre en su PATH\n" -#: ../src/solver/solver.cxx:765 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:772 +#, c++-format msgid "Couldn't add Monitor: {:g} is not a multiple of {:g}!" msgstr "No se pudo añadir el Monitor: {:g} no és multiplo de {:g}!" -#: ../src/sys/expressionparser.cxx:273 +#: ../src/sys/expressionparser.cxx:312 #, c++-format msgid "" "Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so " @@ -557,175 +630,182 @@ msgid "" "may need to change your input file.\n" "{}" msgstr "" +"No se pudo encontrar el generador '{}'. Las expresiones de BOUT++ ahora distinguen entre mayúsculas y minúsculas, por lo que\n" +"puede que tenga que cambiar su archivo de entrada.\n" +"{}" -#: ../src/mesh/mesh.cxx:568 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:587 +#, c++-format msgid "Couldn't find region {:s} in regionMap2D" msgstr "No se pudo encontrar la región {:s} en regionMap2D" -#: ../src/mesh/mesh.cxx:560 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:571 ../src/mesh/mesh.cxx:579 +#, c++-format msgid "Couldn't find region {:s} in regionMap3D" msgstr "No se pudo encontrar la región {:s} en regionMap2D" -#: ../src/mesh/mesh.cxx:576 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:595 +#, c++-format msgid "Couldn't find region {:s} in regionMapPerp" msgstr "No se pudo encontrar la región {:s} en regionMapPerp" #. Convert any exceptions to something a bit more useful -#: ../src/sys/options.cxx:336 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:361 +#, c++-format msgid "Couldn't get {} from option {:s} = '{:s}': {}" -msgstr "No se pudo recuperar el entero de la opción {:s} = '{:s}'" +msgstr "No se pudo obtener {} de la opción {:s} = '{:s}': {}" -#: ../src/bout++.cxx:508 -#, fuzzy, c++-format +#: ../src/bout++.cxx:515 +#, c++-format msgid "DataDir \"{:s}\" does not exist or is not accessible\n" -msgstr "DataDir \"{:s}\" no existe o no es accessible\n" +msgstr "" +"DataDir \"{:s}\" no existe o no es accessible\n" -#: ../src/bout++.cxx:505 -#, fuzzy, c++-format +#: ../src/bout++.cxx:512 +#, c++-format msgid "DataDir \"{:s}\" is not a directory\n" -msgstr "DataDir \"{:s}\" no es un directorio\n" +msgstr "" +"DataDir \"{:s}\" no es un directorio\n" -#: ../src/solver/solver.cxx:665 +#: ../src/solver/solver.cxx:671 msgid "ERROR: Solver is already initialised\n" -msgstr "ERROR: el Solver ya se encuentra inicializado\n" +msgstr "" +"ERROR: el Solver ya se encuentra inicializado\n" -#: ../src/bout++.cxx:209 -#, fuzzy, c++-format +#: ../src/bout++.cxx:216 +#, c++-format msgid "Error encountered during initialisation: {:s}\n" -msgstr "Error encontrado durante la inicialización:{:s}\n" +msgstr "" +"Error encontrado durante la inicialización:{:s}\n" -#: ../src/bout++.cxx:744 +#: ../src/bout++.cxx:751 msgid "Error whilst writing settings" msgstr "Error durante el paso de opciones" -#: ../src/mesh/impls/bout/boutmesh.cxx:332 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:323 +#, c++-format msgid "Error: nx must be greater than 2 times MXG (2 * {:d})" msgstr "Error: nx debe ser mayor que 2 veces MXG (2 * {:d})" -#: ../src/solver/solver.cxx:512 +#: ../src/solver/solver.cxx:520 msgid "Failed to initialise solver-> Aborting\n" -msgstr "Fallo en inicializar el solver-> Abortando\n" +msgstr "" +"Fallo en inicializar el solver-> Abortando\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:290 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:281 +#, c++-format msgid "Finding value for NXPE (ideal = {:f})\n" -msgstr "Encontrando valor para NXPE (ideal = {:f})\n" +msgstr "" +"Encontrando valor para NXPE (ideal = {:f})\n" -#: ../src/solver/solver.cxx:668 +#: ../src/solver/solver.cxx:674 msgid "Initialising solver\n" -msgstr "Initializando el solver\n" +msgstr "" +"Initializando el solver\n" -#: ../src/bout++.cxx:494 +#: ../src/bout++.cxx:501 msgid "" "Input and output file for settings must be different.\n" "Provide -o to avoid this issue.\n" msgstr "" -"Archivos de entrada y salida (`input/output`) para las opciones deben ser " -"diferentes.\n" +"Archivos de entrada y salida (`input/output`) para las opciones deben ser diferentes.\n" "Añada -o para evitar este problema.\n" #: ../src/sys/optionsreader.cxx:76 msgid "Invalid command line option '-' found - maybe check whitespace?" -msgstr "" +msgstr "Invalid command line option '-' found - maybe check whitespace?" -#: ../src/mesh/impls/bout/boutmesh.cxx:400 +#: ../src/mesh/impls/bout/boutmesh.cxx:436 msgid "Loading mesh" msgstr "Cargando malla `mesh`" -#: ../src/mesh/impls/bout/boutmesh.cxx:415 +#: ../src/mesh/impls/bout/boutmesh.cxx:451 msgid "Mesh must contain nx" msgstr "La malla `mesh` debe contener nx" -#: ../src/mesh/impls/bout/boutmesh.cxx:419 +#: ../src/mesh/impls/bout/boutmesh.cxx:455 msgid "Mesh must contain ny" msgstr "La malla `mesh` debe contener ny" #. Not found -#: ../src/mesh/mesh.cxx:536 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:547 +#, c++-format msgid "Missing integer array {:s}\n" -msgstr "Fala la matriz entera {:s}\n" +msgstr "" +"Fala la matriz entera {:s}\n" -#: ../src/solver/solver.cxx:905 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:911 +#, c++-format msgid "Monitor signalled to quit (exception {})\n" -msgstr "Monitor indicó salir\n" +msgstr "" +"El monitor indicó salir (excepción {})\n" -#: ../src/solver/solver.cxx:883 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:889 +#, c++-format msgid "Monitor signalled to quit (return code {})" -msgstr "Monitor indicó salir" +msgstr "El monitor indicó salir (código de retorno {})" -#: ../src/bout++.cxx:823 +#: ../src/bout++.cxx:834 msgid "Name of file whose existence triggers a stop" -msgstr "" +msgstr "Name of file whose existence triggers a stop" -#: ../src/mesh/impls/bout/boutmesh.cxx:565 +#: ../src/mesh/impls/bout/boutmesh.cxx:601 msgid "No boundary regions in this processor" msgstr "Sin regiones de frontera en este procesador" -#: ../src/mesh/impls/bout/boutmesh.cxx:550 -#, fuzzy +#: ../src/mesh/impls/bout/boutmesh.cxx:586 msgid "No boundary regions; domain is periodic\n" -msgstr "Sin regiones de frontera en este procesador" +msgstr "" +"No hay regiones de frontera; el dominio es periódico\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:254 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:245 +#, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n" msgstr "" -"Número de procesadores ({:d}) no divisible para NPs en la dirección x " -"({:d})\n" +"Número de procesadores ({:d}) no divisible para NPs en la dirección x ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:267 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:258 +#, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n" msgstr "" -"Número de procesadores ({:d}) no divisible para NPs en la dirección x " -"({:d})\n" +"Número de procesadores ({:d}) no divisible para NPs en la dirección x ({:d})\n" #. Less than 2 time-steps left -#: ../src/bout++.cxx:896 -#, fuzzy, c++-format +#: ../src/bout++.cxx:908 +#, c++-format msgid "Only {:e} seconds ({:.2f} steps) left. Quitting\n" -msgstr "Solo faltan {:e} segundos. Saliendo\n" +msgstr "" +"Solo quedan {:e} segundos ({:.2f} pasos). Saliendo\n" -#: ../src/sys/options.cxx:303 ../src/sys/options.cxx:345 -#: ../src/sys/options.cxx:393 ../src/sys/options.cxx:428 -#: ../src/sys/options.cxx:703 ../src/sys/options.cxx:730 -#: ../src/sys/options.cxx:757 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:382 ../src/sys/options.cxx:398 +#: ../src/sys/options.cxx:441 ../src/sys/options.cxx:471 +#: ../src/sys/options.cxx:745 ../src/sys/options.cxx:767 +#: ../src/sys/options.cxx:789 +#, c++-format msgid "Option {:s} has no value" msgstr "Opción {:s} sin valor" #. Doesn't exist -#: ../src/sys/options.cxx:159 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:172 +#, c++-format msgid "Option {:s}:{:s} does not exist" msgstr "Opción {:s}:{:s} no existe" -#: ../include/bout/options.hxx:828 -#, fuzzy, c++-format +#: ../include/bout/options.hxx:904 +#, c++-format msgid "" "Options: Setting a value from same source ({:s}) to new value '{:s}' - old " "value was '{:s}'." -msgstr "" -"Opciones: Cambiando valor de la misma fuente ({:s}) al valor nuevo '{:s}' - " -"valor anterior era '{:s}'." +msgstr "Opciones: estableciendo un valor desde la misma fuente ({:s}) al nuevo valor '{:s}' - el valor anterior era '{:s}'." -#: ../src/mesh/impls/bout/boutmesh.cxx:552 -#, fuzzy +#: ../src/mesh/impls/bout/boutmesh.cxx:588 msgid "Possible boundary regions are: " msgstr "Sin regiones de frontera en este procesador" -#: ../src/bout++.cxx:538 -#, fuzzy, c++-format +#: ../src/bout++.cxx:545 +#, c++-format msgid "" "Processor number: {:d} of {:d}\n" "\n" @@ -733,32 +813,33 @@ msgstr "" "Procesador número: {:d} de {:d}\n" "\n" -#: ../src/mesh/mesh.cxx:609 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:642 +#, c++-format msgid "Registered region 2D {:s}" msgstr "Región 2D registrada {:s}" -#: ../src/mesh/mesh.cxx:599 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:632 +#, c++-format msgid "Registered region 3D {:s}" msgstr "Región 3D registrada {:s}" -#: ../src/mesh/mesh.cxx:619 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:652 +#, c++-format msgid "Registered region Perp {:s}" msgstr "Región Perp registrada {:s}" -#: ../src/bout++.cxx:529 -#, fuzzy, c++-format +#: ../src/bout++.cxx:536 +#, c++-format msgid "Revision: {:s}\n" -msgstr "Revisión: {:s}\n" +msgstr "" +"Revisión: {:s}\n" -#: ../src/solver/solver.cxx:581 +#: ../src/solver/solver.cxx:587 msgid "Run time : " msgstr "Tiempo de ejecución : " #. / Run the solver -#: ../src/solver/solver.cxx:525 +#: ../src/solver/solver.cxx:533 msgid "" "Running simulation\n" "\n" @@ -768,80 +849,73 @@ msgstr "" #: ../tests/unit/src/test_bout++.cxx:359 msgid "Signal" -msgstr "" +msgstr "Signal" -#: ../src/bout++.cxx:865 +#: ../src/bout++.cxx:876 msgid "" "Sim Time | RHS evals | Wall Time | Calc Inv Comm I/O SOLVER\n" "\n" msgstr "" -"Tiempo Sim | RHS eval. | Tiempo Wall | Calc Inv Com I/O " -"SOLVER\n" +"Tiempo Sim | RHS eval. | Tiempo Wall | Calc Inv Com I/O SOLVER\n" "\n" -#: ../src/bout++.cxx:868 +#: ../src/bout++.cxx:879 msgid "" "Sim Time | RHS_e evals | RHS_I evals | Wall Time | Calc Inv " "Comm I/O SOLVER\n" "\n" msgstr "" -"Tiempo Sim | RHS_e eval. | RHS_I eval. | Tiempo Wall | Calc Inv " -"Com I/O SOLVER\n" +"Tiempo Sim | RHS_e eval. | RHS_I eval. | Tiempo Wall | Calc Inv Com I/O SOLVER\n" "\n" -#: ../src/solver/solver.cxx:506 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:514 +#, c++-format msgid "Solver running for {:d} outputs with monitor timestep of {:e}\n" msgstr "" -"Solver corriendo para {:d} outputs con intervalos de tiempo de monitor de " -"{:e}\n" +"El solucionador se ejecuta para {:d} salidas con un paso de tiempo de monitor de {:e}\n" -#: ../src/solver/solver.cxx:502 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:510 +#, c++-format msgid "Solver running for {:d} outputs with output timestep of {:e}\n" msgstr "" -"Solver corriendo para {:d} outputs con intervalos de tiempo de output de " -"{:e}\n" +"Solver corriendo para {:d} outputs con intervalos de tiempo de output de {:e}\n" -#: ../src/solver/solver.cxx:781 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:788 +#, c++-format msgid "" "Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) after init is " "called!" -msgstr "" -"Solver::addMonitor: No se puedo reducir el intervalo de tiempo (de {:g} a " -"{:g}) después de que init fuera llamado!" +msgstr "Solver::addMonitor: No se puedo reducir el intervalo de tiempo (de {:g} a {:g}) después de que init fuera llamado!" -#: ../src/solver/solver.cxx:1281 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:1289 +#, c++-format msgid "" "Time derivative at wrong location - Field is at {:s}, derivative is at {:s} " "for field '{:s}'\n" msgstr "" -"Derivada del tiempo en lugar erróneo - El field se encuentra en {:s}, la " -"derivada se encuentra en {:s} para el field '{:s}'\n" +"Derivada temporal en ubicación incorrecta: el campo está en {:s}, la derivada está en {:s} para el campo '{:s}'\n" -#: ../src/solver/solver.cxx:1480 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:1494 +#, c++-format msgid "Time derivative for variable '{:s}' not set" msgstr "Derivada del tiempo para la variable '{:s}' no fijada" -#: ../src/mesh/mesh.cxx:605 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:638 +#, c++-format msgid "Trying to add an already existing region {:s} to regionMap2D" msgstr "Intentando añadir una región ya existente {:s} a regionMap2D" -#: ../src/mesh/mesh.cxx:595 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:614 +#, c++-format msgid "Trying to add an already existing region {:s} to regionMap3D" msgstr "Intentando añadir una región ya existente {:s} a regionMap3D" -#: ../src/mesh/mesh.cxx:616 -#, fuzzy, c++-format +#: ../src/mesh/mesh.cxx:649 +#, c++-format msgid "Trying to add an already existing region {:s} to regionMapPerp" msgstr "Intentando añadir una región ya existente {:s} a regionMapPerp" -#: ../src/sys/options.cxx:99 ../src/sys/options.cxx:138 +#: ../src/sys/options.cxx:112 ../src/sys/options.cxx:151 #, c++-format msgid "" "Trying to index Option '{0}' with '{1}', but '{0}' is a value, not a " @@ -849,8 +923,10 @@ msgid "" "This is likely the result of clashing input options, and you may have to " "rename one of them.\n" msgstr "" +"Se está intentando indexar la opción '{0}' con '{1}', pero '{0}' es un valor, no una sección.\n" +"Es probable que esto sea el resultado de opciones de entrada en conflicto, y puede que tenga que cambiar el nombre de una de ellas.\n" -#: ../src/mesh/coordinates.cxx:1462 +#: ../src/mesh/coordinates.cxx:1464 msgid "" "Unrecognised paralleltransform option.\n" "Valid choices are 'identity', 'shifted', 'fci'" @@ -858,155 +934,177 @@ msgstr "" "Opción paralleltransform desconocida.\n" "Opciones válidas son 'identity', 'shifted', 'fci'" -#: ../src/sys/options.cxx:872 +#: ../src/sys/options.cxx:899 msgid "Unused options:\n" -msgstr "Opciones sin usar:\n" +msgstr "" +"Opciones sin usar:\n" -#: ../src/bout++.cxx:439 -#, fuzzy, c++-format +#: ../src/bout++.cxx:446 +#, c++-format msgid "Usage is {:s} -d \n" -msgstr "Correcto uso es {:s} -d \n" +msgstr "" +"Correcto uso es {:s} -d \n" -#: ../src/bout++.cxx:448 -#, fuzzy, c++-format +#: ../src/bout++.cxx:455 +#, c++-format msgid "Usage is {:s} -f \n" -msgstr "Correcto uso es {:s} -f \n" +msgstr "" +"Correcto uso es {:s} -f \n" -#: ../src/bout++.cxx:466 -#, fuzzy, c++-format +#: ../src/bout++.cxx:473 +#, c++-format msgid "Usage is {:s} -l \n" -msgstr "Correcto uso es {:s} -l \n" +msgstr "" +"Correcto uso es {:s} -l \n" -#: ../src/bout++.cxx:457 -#, fuzzy, c++-format +#: ../src/bout++.cxx:464 +#, c++-format msgid "Usage is {:s} -o \n" -msgstr "Correcto uso es {:s} -o \n" +msgstr "" +"Correcto uso es {:s} -o \n" -#: ../src/bout++.cxx:353 -#, fuzzy, c++-format +#: ../src/bout++.cxx:360 +#, c++-format msgid "Usage is {} {} \n" -msgstr "Correcto uso es {:s} -l \n" +msgstr "" +"Uso: {} {} \n" #: ../tests/unit/src/test_bout++.cxx:32 ../tests/unit/src/test_bout++.cxx:46 msgid "Usage:" -msgstr "" +msgstr "Usage:" #. Print help message -- note this will be displayed once per processor as we've not #. started MPI yet. -#: ../src/bout++.cxx:367 -#, fuzzy, c++-format +#: ../src/bout++.cxx:374 +#, c++-format msgid "" "Usage: {:s} [-d ] [-f ] [restart [append]] " "[VAR=VALUE]\n" msgstr "" -"Uso: {:s} [-d ] [-f ] [restart [append]] " -"[VAR=VALUE]\n" +"Uso: {:s} [-d ] [-f ] [restart [append]] [VAR=VALUE]\n" #. restart file should be written by physics model -#: ../src/solver/solver.cxx:921 -#, fuzzy +#: ../src/solver/solver.cxx:927 msgid "User signalled to quit. Returning\n" -msgstr "Monitor indicó salir\n" +msgstr "" +"Monitor indicó salir\n" -#: ../src/sys/options.cxx:373 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:486 +#, c++-format +msgid "Value for option {:s} = {:e} is not a bool" +msgstr "Valor para la opción {:s} = {:e} no es un entero" + +#: ../src/sys/options.cxx:426 +#, c++-format msgid "Value for option {:s} = {:e} is not an integer" msgstr "Valor para la opción {:s} = {:e} no es un entero" -#: ../src/sys/options.cxx:408 +#: ../src/sys/options.cxx:456 #, c++-format msgid "Value for option {:s} cannot be converted to a BoutReal" -msgstr "" +msgstr "El valor de la opción {:s} no se puede convertir a un BoutReal" -#: ../src/sys/options.cxx:581 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:623 +#, c++-format msgid "Value for option {:s} cannot be converted to a Field2D" -msgstr "Valor para la opción {:s} = {:e} no es un entero" +msgstr "El valor de la opción {:s} no se puede convertir a un Field2D" -#: ../src/sys/options.cxx:529 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:571 +#, c++-format msgid "Value for option {:s} cannot be converted to a Field3D" -msgstr "Valor para la opción {:s} = {:e} no es un entero" +msgstr "El valor de la opción {:s} no se puede convertir a un Field3D" -#: ../src/sys/options.cxx:663 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:705 +#, c++-format msgid "Value for option {:s} cannot be converted to a FieldPerp" -msgstr "Valor para la opción {:s} = {:e} no es un entero" +msgstr "El valor de la opción {:s} no se puede convertir a un FieldPerp" -#: ../src/sys/options.cxx:451 +#: ../src/sys/options.cxx:491 #, c++-format msgid "Value for option {:s} cannot be converted to a bool" -msgstr "" +msgstr "El valor de la opción {:s} no se puede convertir a un bool" -#: ../src/sys/options.cxx:709 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:751 +#, c++-format msgid "Value for option {:s} cannot be converted to an Array" -msgstr "Valor para la opción {:s} = {:e} no es un entero" +msgstr "El valor de la opción {:s} no se puede convertir a un Array" -#: ../src/sys/options.cxx:736 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:773 +#, c++-format msgid "Value for option {:s} cannot be converted to an Matrix" -msgstr "Valor para la opción {:s} = {:e} no es un entero" +msgstr "El valor de la opción {:s} no se puede convertir a una Matrix" -#: ../src/sys/options.cxx:763 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:795 +#, c++-format msgid "Value for option {:s} cannot be converted to an Tensor" -msgstr "Valor para la opción {:s} = {:e} no es un entero" +msgstr "El valor de la opción {:s} no se puede convertir a un Tensor" #. Another type which can't be converted -#: ../src/sys/options.cxx:365 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:418 +#, c++-format msgid "Value for option {:s} is not an integer" -msgstr "Valor para la opción {:s} = {:e} no es un entero" +msgstr "El valor de la opción {:s} no es un entero" -#: ../src/solver/solver.cxx:1232 ../src/solver/solver.cxx:1238 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:1240 ../src/solver/solver.cxx:1246 +#, c++-format msgid "Variable '{:s}' not initialised" msgstr "Variable '{:s}' sin inicializar" -#: ../src/mesh/impls/bout/boutmesh.cxx:431 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:467 +#, c++-format msgid "" "WARNING: Number of toroidal points should be 2^n for efficient FFT " "performance -- consider changing MZ ({:d}) if using FFTs\n" msgstr "" -"WARNING: el número de puntos toroidales debería ser 2^n para una FFT " -"eficiente -- considere cambiar MZ si se usan FFTs\n" +"ADVERTENCIA: el número de puntos toroidales debe ser 2^n para un rendimiento eficiente de FFT; considere cambiar MZ ({:d}) si usa FFT\n" -#: ../src/mesh/coordinates.cxx:633 +#: ../src/mesh/coordinates.cxx:635 msgid "WARNING: extrapolating input mesh quantities into x-boundary cells\n" msgstr "" +"WARNING: extrapolating input mesh quantities into x-boundary cells\n" -#: ../src/mesh/coordinates.cxx:410 +#: ../src/mesh/coordinates.cxx:412 msgid "" "WARNING: extrapolating input mesh quantities into x-boundary cells. Set " "option extrapolate_x=false to disable this.\n" msgstr "" +"WARNING: extrapolating input mesh quantities into x-boundary cells. Set option extrapolate_x=false to disable this.\n" -#: ../src/mesh/coordinates.cxx:638 +#: ../src/mesh/coordinates.cxx:640 msgid "WARNING: extrapolating input mesh quantities into y-boundary cells\n" msgstr "" +"WARNING: extrapolating input mesh quantities into y-boundary cells\n" -#: ../src/mesh/coordinates.cxx:415 +#: ../src/mesh/coordinates.cxx:417 msgid "" "WARNING: extrapolating input mesh quantities into y-boundary cells. Set " "option extrapolate_y=false to disable this.\n" msgstr "" +"WARNING: extrapolating input mesh quantities into y-boundary cells. Set option extrapolate_y=false to disable this.\n" -#: ../src/bout++.cxx:814 +#: ../src/bout++.cxx:825 msgid "Wall time limit in hours. By default (< 0), no limit" -msgstr "" +msgstr "Wall time limit in hours. By default (< 0), no limit" #: ../src/sys/optionsreader.cxx:42 -#, fuzzy, c++-format +#, c++-format msgid "Writing options to file {:s}\n" -msgstr "Escribiendo opciones a archivo {:s}\n" +msgstr "" +"Escribiendo opciones a archivo {:s}\n" #. / The source label given to default values -#: ../src/sys/options.cxx:15 +#: ../src/sys/options.cxx:34 msgid "default" msgstr "por defecto" +#, fuzzy, c++-format +#~ msgid "\tOpenMP parallelisation {}" +#~ msgstr "\tParalelización en OpenMP desactivada\n" + +#, fuzzy, c++-format +#~ msgid "\tOption '{:s}': Boolean expected. Got '{:s}'\n" +#~ msgstr "\tOpción '{:s}': valor Booleano esperado. Se obtuvo '{:s}'\n" + #~ msgid "\tChecking disabled\n" #~ msgstr "\tComprobación desactivada\n" @@ -1014,11 +1112,6 @@ msgstr "por defecto" #~ msgid "\tChecking enabled, level {:d}\n" #~ msgstr "\tComprobación activada, nivel {:d}\n" -#, fuzzy -#~ msgid "\tOpenMP parallelisation enabled, using {:d} threads\n" -#~ msgstr "" -#~ "\tParalelización en OpenMP activada, usando {:d} procesos (`threads`)\n" - #~ msgid "\tSignal handling enabled\n" #~ msgstr "\tGestión de señal activada\n" diff --git a/locale/fr/libbout.po b/locale/fr/libbout.po index ae88b8953c..8b8ca69328 100644 --- a/locale/fr/libbout.po +++ b/locale/fr/libbout.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: BOUT++ 4.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-12 09:17+0100\n" +"POT-Creation-Date: 2025-08-13 23:37+0100\n" "PO-Revision-Date: 2018-10-21 22:46+0100\n" "Last-Translator: \n" "Language-Team: French\n" @@ -17,109 +17,131 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:191 +#: ../src/mesh/impls/bout/boutmesh.cxx:182 #, c++-format msgid "" "\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" +"\t -> région Core jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) doit être un multiple de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:224 +#: ../src/mesh/impls/bout/boutmesh.cxx:215 #, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" +"\t -> région Core jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) doit être un multiple de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:199 +#: ../src/mesh/impls/bout/boutmesh.cxx:190 #, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" +"\t -> région Core jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) doit être un multiple de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:309 +#: ../src/mesh/impls/bout/boutmesh.cxx:300 msgid "\t -> Good value\n" msgstr "" +"\t -> Valeur correcte\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:180 +#: ../src/mesh/impls/bout/boutmesh.cxx:171 #, c++-format msgid "" "\t -> Leg region jyseps1_1+1 ({:d}) must be a multiple of MYSUB ({:d})\n" msgstr "" +"\t -> région Leg jyseps1_1+1 ({:d}) doit être un multiple de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:215 +#: ../src/mesh/impls/bout/boutmesh.cxx:206 #, c++-format msgid "" "\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" +"\t -> région leg jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) doit être un multiple de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:232 +#: ../src/mesh/impls/bout/boutmesh.cxx:223 #, c++-format msgid "" "\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a multiple of " "MYSUB ({:d})\n" msgstr "" +"\t -> région leg ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) doit être un multiple de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:208 +#: ../src/mesh/impls/bout/boutmesh.cxx:199 #, c++-format msgid "" "\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" +"\t -> région leg ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) doit être un multiple de MYSUB ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:175 +#: ../src/mesh/impls/bout/boutmesh.cxx:166 #, c++-format msgid "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n" msgstr "" +"\t -> ny/NYPE ({:d}/{:d} = {:d}) doit être >= MYG ({:d})\n" #: ../src/bout++.cxx:575 #, c++-format +msgid "\tADIOS2 support {}\n" +msgstr "" +"\tPrise en charge de ADIOS2 {}\n" + +#: ../src/bout++.cxx:583 +#, c++-format msgid "\tBacktrace in exceptions {}\n" msgstr "" +"\tTrace d'appel dans les exceptions {}\n" #. Loop over all possibilities #. Processors divide equally #. Mesh in X divides equally #. Mesh in Y divides equally -#: ../src/mesh/impls/bout/boutmesh.cxx:297 +#: ../src/mesh/impls/bout/boutmesh.cxx:288 #, c++-format msgid "\tCandidate value: {:d}\n" msgstr "" +"\tValeur candidate : {:d}\n" -#: ../src/bout++.cxx:576 +#: ../src/bout++.cxx:584 #, c++-format msgid "\tColour in logs {}\n" msgstr "" +"\tCouleur dans les journaux {}\n" -#: ../src/bout++.cxx:594 +#: ../src/bout++.cxx:599 msgid "\tCommand line options for this run : " -msgstr "" +msgstr "\tOptions de ligne de commande pour cette exécution : " #. The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings #. which could cause problems (e.g. terminate strings). -#: ../src/bout++.cxx:590 +#: ../src/bout++.cxx:595 #, c++-format msgid "\tCompiled with flags : {:s}\n" msgstr "" +"\tCompilé avec les options : {:s}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:324 +#: ../src/mesh/impls/bout/boutmesh.cxx:315 #, c++-format msgid "" "\tDomain split (NXPE={:d}, NYPE={:d}) into domains (localNx={:d}, " "localNy={:d})\n" msgstr "" +"\tDomaine découpé (NXPE={:d}, NYPE={:d}) en sous-domaines (localNx={:d}, localNy={:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:364 +#: ../src/mesh/impls/bout/boutmesh.cxx:357 #, c++-format msgid "\tERROR: Cannot split {:d} Y points equally between {:d} processors\n" msgstr "" +"\tERREUR : impossible de répartir {:d} points Y équitablement entre {:d} processeurs\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:372 +#: ../src/mesh/impls/bout/boutmesh.cxx:365 #, c++-format msgid "\tERROR: Cannot split {:d} Z points equally between {:d} processors\n" msgstr "" +"\tERREUR : impossible de répartir {:d} points Z équitablement entre {:d} processeurs\n" #: ../src/sys/options/options_ini.cxx:200 #, c++-format @@ -127,39 +149,46 @@ msgid "" "\tEmpty key\n" "\tLine: {:s}" msgstr "" +"\tClé vide\n" +"\tLigne : {:s}" #: ../src/sys/optionsreader.cxx:127 #, c++-format msgid "\tEmpty key or value in command line '{:s}'\n" msgstr "" +"\tClé ou valeur vide dans la ligne de commande '{:s}'\n" -#: ../src/bout++.cxx:582 +#: ../src/bout++.cxx:587 #, c++-format msgid "\tExtra debug output {}\n" msgstr "" +"\tSortie de débogage supplémentaire {}\n" -#: ../src/bout++.cxx:561 +#: ../src/bout++.cxx:568 #, c++-format msgid "\tFFT support {}\n" msgstr "" +"\tPrise en charge de FFT {}\n" -#: ../src/bout++.cxx:585 +#: ../src/bout++.cxx:590 #, c++-format msgid "\tField name tracking {}\n" msgstr "" +"\tSuivi des noms de champ {}\n" -#: ../src/bout++.cxx:583 +#: ../src/bout++.cxx:588 #, c++-format msgid "\tFloating-point exceptions {}\n" msgstr "" +"\tExceptions en virgule flottante {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:440 +#: ../src/mesh/impls/bout/boutmesh.cxx:476 msgid "\tGrid size: " -msgstr "" +msgstr "\tTaille de la grille : " -#: ../src/mesh/impls/bout/boutmesh.cxx:463 +#: ../src/mesh/impls/bout/boutmesh.cxx:499 msgid "\tGuard cells (x,y,z): " -msgstr "" +msgstr "\tCellules de garde (x,y,z) : " #: ../src/sys/options/options_ini.cxx:204 #, c++-format @@ -167,139 +196,159 @@ msgid "" "\tKey must not contain ':' character\n" "\tLine: {:s}" msgstr "" +"\tLa clé ne doit pas contenir le caractère ':'\n" +"\tLigne : {:s}" -#: ../src/bout++.cxx:563 +#: ../src/bout++.cxx:570 #, c++-format msgid "\tLAPACK support {}\n" msgstr "" +"\tPrise en charge de LAPACK {}\n" -#: ../src/bout++.cxx:586 +#: ../src/bout++.cxx:591 #, c++-format msgid "\tMessage stack {}\n" msgstr "" +"\tPile de messages {}\n" -#: ../src/bout++.cxx:560 +#: ../src/bout++.cxx:567 #, c++-format msgid "\tMetrics mode is {}\n" msgstr "" +"\tLe mode de métriques est {}\n" #: ../src/sys/optionsreader.cxx:111 #, c++-format msgid "\tMultiple '=' in command-line argument '{:s}'\n" msgstr "" +"\tPlusieurs '=' dans l'argument de ligne de commande '{:s}'\n" -#: ../src/bout++.cxx:562 +#: ../src/bout++.cxx:569 #, c++-format msgid "\tNatural language support {}\n" msgstr "" +"\tPrise en charge des langues naturelles {}\n" -#: ../src/bout++.cxx:567 +#: ../src/bout++.cxx:574 #, c++-format msgid "\tNetCDF support {}{}\n" msgstr "" +"\tPrise en charge de NetCDF {}{}\n" -#: ../src/bout++.cxx:577 +#: ../src/bout++.cxx:585 #, c++-format -msgid "\tOpenMP parallelisation {}" +msgid "\tOpenMP parallelisation {}, using {} threads\n" msgstr "" +"\tParallélisation OpenMP {}, avec {} threads\n" #. Mark the option as used #. Option not found -#: ../src/sys/options.cxx:311 ../src/sys/options.cxx:380 -#: ../src/sys/options.cxx:415 ../src/sys/options.cxx:457 -#: ../src/sys/options.cxx:717 ../src/sys/options.cxx:744 -#: ../src/sys/options.cxx:771 ../include/bout/options.hxx:516 -#: ../include/bout/options.hxx:549 ../include/bout/options.hxx:573 -#: ../include/bout/options.hxx:820 +#: ../include/bout/options.hxx:586 ../include/bout/options.hxx:619 +#: ../include/bout/options.hxx:643 ../include/bout/options.hxx:896 msgid "\tOption " -msgstr "" +msgstr "\tOption " -#: ../src/sys/options.cxx:447 +#: ../src/sys/options.cxx:369 #, c++-format -msgid "\tOption '{:s}': Boolean expected. Got '{:s}'\n" -msgstr "" +msgid "\tOption {} = {}" +msgstr "\tOption {} = {}" #: ../src/sys/options/options_ini.cxx:70 #, c++-format msgid "\tOptions file '{:s}' not found\n" msgstr "" +"\tFichier d'options '{:s}' introuvable\n" -#: ../src/bout++.cxx:568 +#: ../src/bout++.cxx:576 #, c++-format msgid "\tPETSc support {}\n" msgstr "" +"\tPrise en charge de PETSc {}\n" -#: ../src/bout++.cxx:571 +#: ../src/bout++.cxx:579 #, c++-format msgid "\tPVODE support {}\n" msgstr "" +"\tPrise en charge de PVODE {}\n" -#: ../src/bout++.cxx:557 +#: ../src/bout++.cxx:564 msgid "\tParallel NetCDF support disabled\n" msgstr "" +"\tPrise en charge de NetCDF parallèle désactivée\n" -#: ../src/bout++.cxx:555 +#: ../src/bout++.cxx:562 msgid "\tParallel NetCDF support enabled\n" msgstr "" +"\tPrise en charge de NetCDF parallèle activée\n" -#: ../src/bout++.cxx:569 +#: ../src/bout++.cxx:577 #, c++-format msgid "\tPretty function name support {}\n" msgstr "" +"\tPrise en charge des noms de fonctions détaillés {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:437 +#: ../src/mesh/impls/bout/boutmesh.cxx:473 msgid "\tRead nz from input grid file\n" msgstr "" +"\tnz lu depuis le fichier de grille d'entrée\n" -#: ../src/mesh/mesh.cxx:238 +#: ../src/mesh/mesh.cxx:249 msgid "\tReading contravariant vector " -msgstr "" +msgstr "\tLecture du vecteur contravariant " -#: ../src/mesh/mesh.cxx:231 ../src/mesh/mesh.cxx:252 +#: ../src/mesh/mesh.cxx:242 ../src/mesh/mesh.cxx:263 msgid "\tReading covariant vector " -msgstr "" +msgstr "\tLecture du vecteur covariant " -#: ../src/bout++.cxx:548 +#: ../src/bout++.cxx:555 #, c++-format msgid "\tRuntime error checking {}" -msgstr "" +msgstr "\tVérification des erreurs à l'exécution {}" -#: ../src/bout++.cxx:573 +#: ../src/bout++.cxx:581 #, c++-format msgid "\tSLEPc support {}\n" msgstr "" +"\tPrise en charge de SLEPc {}\n" -#: ../src/bout++.cxx:574 +#: ../src/bout++.cxx:582 #, c++-format msgid "\tSUNDIALS support {}\n" msgstr "" +"\tPrise en charge de SUNDIALS {}\n" -#: ../src/bout++.cxx:572 +#: ../src/bout++.cxx:580 #, c++-format msgid "\tScore-P support {}\n" msgstr "" +"\tPrise en charge de Score-P {}\n" -#: ../src/bout++.cxx:584 -#, fuzzy, c++-format +#: ../src/bout++.cxx:589 +#, c++-format msgid "\tSignal handling support {}\n" -msgstr "\tTraitement du signal désactivé\n" +msgstr "" +"\tPrise en charge de la gestion des signaux {}\n" #: ../src/solver/impls/split-rk/split-rk.cxx:76 #, c++-format msgid "\tUsing a timestep {:e}\n" msgstr "" +"\tUtilisation d'un pas de temps {:e}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:577 +#: ../src/mesh/impls/bout/boutmesh.cxx:613 msgid "\tdone\n" msgstr "" +"\tterminé\n" #: ../src/solver/impls/split-rk/split-rk.cxx:41 msgid "" "\n" "\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n" msgstr "" +"\n" +"\tSolveur fractionné Runge-Kutta-Legendre et SSP-RK3\n" -#: ../src/bout++.cxx:371 +#: ../src/bout++.cxx:378 msgid "" "\n" " -d \t\tLook in for input/output files\n" @@ -309,37 +358,47 @@ msgid "" " -v, --verbose\t\t\tIncrease verbosity\n" " -q, --quiet\t\t\tDecrease verbosity\n" msgstr "" +"\n" +" -d \t\tChercher les fichiers d'entrée/sortie dans \n" +" -f \t\tUtiliser les OPTIONS indiquées dans \n" +" -o \tEnregistrer les OPTIONS utilisées dans \n" +" -l, --log \tÉcrire le journal dans \n" +" -v, --verbose\t\t\tAugmenter la verbosité\n" +" -q, --quiet\t\t\tRéduire la verbosité\n" -#: ../src/sys/expressionparser.cxx:302 +#: ../src/sys/expressionparser.cxx:341 #, c++-format msgid "" "\n" " {1: ^{2}}{0}\n" " Did you mean '{0}'?" msgstr "" +"\n" +" {1: ^{2}}{0}\n" +" Vouliez-vous dire '{0}' ?" -#: ../src/solver/solver.cxx:580 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:586 +#, c++-format msgid "" "\n" "Run finished at : {:s}\n" msgstr "" "\n" -"L'exécution se termine à {:s}\n" +"Exécution terminée à : {:s}\n" -#: ../src/solver/solver.cxx:532 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:540 +#, c++-format msgid "" "\n" "Run started at : {:s}\n" msgstr "" "\n" -"L'exécution se termine à {:s}\n" +"Exécution démarrée à : {:s}\n" #. Raw string to help with the formatting of the message, and a #. separate variable so clang-format doesn't barf on the #. exception -#: ../src/sys/options.cxx:1102 +#: ../src/sys/options.cxx:1158 msgid "" "\n" "There were unused input options:\n" @@ -366,8 +425,33 @@ msgid "" "\n" "{}" msgstr "" +"\n" +"Des options d'entrée inutilisées ont été trouvées :\n" +"-----\n" +"{:i}\n" +"-----\n" +"Il est possible que certaines options aient été mal saisies. Les arguments\n" +"d'entrée de BOUT++ distinguent désormais les majuscules et les minuscules,\n" +"et certains ont changé de nom. Vous pouvez essayer d'exécuter\n" +"\n" +" /bin/bout-v5-input-file-upgrader.py {}/{}\n" +"\n" +"afin de corriger automatiquement les problèmes les plus courants. Si les\n" +"options ci-dessus ne sont utilisées que dans certains cas selon d'autres\n" +"options, vous pouvez appeler `Options::setConditionallyUsed()`, par exemple :\n" +"\n" +" Options::root()[\"{}\"].setConditionallyUsed();\n" +"\n" +"afin de marquer une section ou une valeur comme dépendant d'autres valeurs\n" +"et de l'ignorer lors de cette vérification. Sinon, si vous êtes certain que\n" +"les entrées ci-dessus ne sont pas une erreur, vous pouvez définir\n" +"'input:error_on_unused_options=false' pour désactiver cette vérification des\n" +"options inutilisées. Vous pouvez également définir 'input:validate=true'\n" +"pour vérifier les entrées sans lancer la simulation complète.\n" +"\n" +"{}" -#: ../src/bout++.cxx:382 +#: ../src/bout++.cxx:389 #, c++-format msgid "" " --print-config\t\tPrint the compile-time configuration\n" @@ -399,117 +483,148 @@ msgid "" "For all possible input parameters, see the user manual and/or the physics " "model source (e.g. {:s}.cxx)\n" msgstr "" +" --print-config\t\tAfficher la configuration de compilation\n" +" --list-solvers\t\tLister les solveurs temporels disponibles\n" +" --help-solver \tAfficher l'aide du solveur temporel indiqué\n" +" --list-laplacians\t\tLister les solveurs d'inversion laplacienne disponibles\n" +" --help-laplacian \tAfficher l'aide du solveur d'inversion laplacienne indiqué\n" +" --list-laplacexz\t\tLister les solveurs d'inversion LaplaceXZ disponibles\n" +" --help-laplacexz \tAfficher l'aide du solveur d'inversion LaplaceXZ indiqué\n" +" --list-invertpars\t\tLister les solveurs InvertPar disponibles\n" +" --help-invertpar \tAfficher l'aide du solveur InvertPar indiqué\n" +" --list-rkschemes\t\tLister les schémas de Runge-Kutta disponibles\n" +" --help-rkscheme \tAfficher l'aide du schéma de Runge-Kutta indiqué\n" +" --list-meshes\t\t\tLister les maillages disponibles\n" +" --help-mesh \t\tAfficher l'aide du maillage indiqué\n" +" --list-xzinterpolations\tLister les interpolations XZ disponibles\n" +" --help-xzinterpolation \tAfficher l'aide de l'interpolation XZ indiquée\n" +" --list-zinterpolations\tLister les interpolations Z disponibles\n" +" --help-zinterpolation \tAfficher l'aide de l'interpolation Z indiquée\n" +" -h, --help\t\t\tCe message\n" +" restart [append]\t\tRedémarrer la simulation. Si append est indiqué, les données sont ajoutées aux fichiers de sortie existants ; sinon, ils sont écrasés\n" +" VAR=VALUE\t\t\tDéfinir VALUE pour le paramètre d'entrée VAR\n" +"\n" +"Pour la liste complète des paramètres d'entrée, consultez le manuel utilisateur et/ou la source du modèle physique (p. ex. {:s}.cxx)\n" -#: ../src/bout++.cxx:379 +#: ../src/bout++.cxx:386 msgid " -c, --color\t\t\tColor output using bout-log-color\n" msgstr "" +" -c, --color\t\t\tSortie colorée avec bout-log-color\n" -#: ../include/bout/options.hxx:823 +#: ../include/bout/options.hxx:899 msgid ") overwritten with:" -msgstr "" +msgstr ") écrasée par :" -#: ../src/bout++.cxx:550 +#: ../src/bout++.cxx:557 #, c++-format msgid ", level {}" -msgstr "" - -#: ../src/bout++.cxx:579 -#, c++-format -msgid ", using {} threads" -msgstr "" +msgstr ", niveau {}" #: ../tests/unit/src/test_bout++.cxx:352 msgid "4 of 8" -msgstr "" +msgstr "4 sur 8" -#: ../src/sys/options.cxx:868 +#: ../src/sys/options.cxx:895 msgid "All options used\n" msgstr "" +"Toutes les options ont été utilisées\n" -#: ../src/bout++.cxx:528 +#: ../src/bout++.cxx:535 #, c++-format msgid "BOUT++ version {:s}\n" msgstr "" +"Version de BOUT++ {:s}\n" -#: ../src/bout++.cxx:143 +#: ../src/bout++.cxx:147 msgid "Bad command line arguments:\n" msgstr "" +"Arguments de ligne de commande incorrects :\n" + +#: ../src/sys/expressionparser.cxx:192 +#, c++-format +msgid "Boolean operator argument {:e} is not a bool" +msgstr "L'argument {:e} de l'opérateur booléen n'est pas un booléen" -#: ../src/mesh/impls/bout/boutmesh.cxx:559 +#: ../src/mesh/impls/bout/boutmesh.cxx:595 msgid "Boundary regions in this processor: " -msgstr "" +msgstr "Régions de bord dans ce processeur : " -#: ../src/mesh/impls/bout/boutmesh.cxx:355 +#: ../src/mesh/impls/bout/boutmesh.cxx:348 #, c++-format msgid "Cannot split {:d} X points equally between {:d} processors\n" msgstr "" +"Impossible de répartir {:d} points X équitablement entre {:d} processeurs\n" -#: ../src/bout++.cxx:818 +#: ../src/bout++.cxx:829 msgid "Check if a file exists, and exit if it does." -msgstr "" +msgstr "Vérifier si un fichier existe, et quitter si c'est le cas." -#: ../src/bout++.cxx:533 -#, fuzzy, c++-format +#: ../src/bout++.cxx:540 +#, c++-format msgid "" "Code compiled on {:s} at {:s}\n" "\n" msgstr "" -"Code compilé le {:s} à {:s}\n" +"Code compilé sur {:s} à {:s}\n" "\n" #: ../src/sys/optionsreader.cxx:130 msgid "Command line" -msgstr "" +msgstr "Ligne de commande" -#: ../src/bout++.cxx:544 ../tests/unit/src/test_bout++.cxx:358 +#: ../src/bout++.cxx:551 ../tests/unit/src/test_bout++.cxx:358 msgid "Compile-time options:\n" msgstr "" +"Options de compilation :\n" #: ../tests/unit/src/test_bout++.cxx:362 msgid "Compiled with flags" -msgstr "" +msgstr "Compilé avec les options" -#: ../src/mesh/impls/bout/boutmesh.cxx:568 +#: ../src/mesh/impls/bout/boutmesh.cxx:604 msgid "Constructing default regions" -msgstr "" +msgstr "Construction des régions par défaut" -#: ../src/bout++.cxx:520 +#: ../src/bout++.cxx:527 #, c++-format msgid "Could not create PID file {:s}" -msgstr "" +msgstr "Impossible de créer le fichier PID {:s}" -#: ../src/mesh/impls/bout/boutmesh.cxx:318 +#: ../src/mesh/impls/bout/boutmesh.cxx:309 msgid "" "Could not find a valid value for NXPE. Try a different number of processors." -msgstr "" +msgstr "Impossible de trouver une valeur valide pour NXPE. Essayez un autre nombre de processeurs." #: ../src/sys/options/options_ini.cxx:160 #, c++-format msgid "Could not open output file '{:s}'\n" msgstr "" +"Impossible d'ouvrir le fichier de sortie '{:s}'\n" -#: ../src/bout++.cxx:652 +#: ../src/bout++.cxx:657 #, c++-format msgid "Could not open {:s}/{:s}.{:d} for writing" -msgstr "" +msgstr "Impossible d'ouvrir {:s}/{:s}.{:d} en écriture" #. Error reading -#: ../src/mesh/mesh.cxx:532 +#: ../src/mesh/mesh.cxx:543 #, c++-format msgid "Could not read integer array '{:s}'\n" msgstr "" +"Impossible de lire le tableau d'entiers '{:s}'\n" #. Failed . Probably not important enough to stop the simulation -#: ../src/bout++.cxx:632 +#: ../src/bout++.cxx:637 msgid "Could not run bout-log-color. Make sure it is in your PATH\n" msgstr "" +"Impossible d'exécuter bout-log-color. Assurez-vous qu'il se trouve dans votre PATH\n" -#: ../src/solver/solver.cxx:765 +#: ../src/solver/solver.cxx:772 #, c++-format msgid "Couldn't add Monitor: {:g} is not a multiple of {:g}!" -msgstr "" +msgstr "Impossible d'ajouter le moniteur : {:g} n'est pas un multiple de {:g} !" -#: ../src/sys/expressionparser.cxx:273 +#: ../src/sys/expressionparser.cxx:312 #, c++-format msgid "" "Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so " @@ -517,196 +632,216 @@ msgid "" "may need to change your input file.\n" "{}" msgstr "" +"Impossible de trouver le générateur '{}'. Les expressions BOUT++ distinguent désormais les majuscules et les minuscules ;\n" +"vous devrez peut-être modifier votre fichier d'entrée.\n" +"{}" -#: ../src/mesh/mesh.cxx:568 +#: ../src/mesh/mesh.cxx:587 #, c++-format msgid "Couldn't find region {:s} in regionMap2D" -msgstr "" +msgstr "Impossible de trouver la région {:s} dans regionMap2D" -#: ../src/mesh/mesh.cxx:560 +#: ../src/mesh/mesh.cxx:571 ../src/mesh/mesh.cxx:579 #, c++-format msgid "Couldn't find region {:s} in regionMap3D" -msgstr "" +msgstr "Impossible de trouver la région {:s} dans regionMap3D" -#: ../src/mesh/mesh.cxx:576 +#: ../src/mesh/mesh.cxx:595 #, c++-format msgid "Couldn't find region {:s} in regionMapPerp" -msgstr "" +msgstr "Impossible de trouver la région {:s} dans regionMapPerp" #. Convert any exceptions to something a bit more useful -#: ../src/sys/options.cxx:336 +#: ../src/sys/options.cxx:361 #, c++-format msgid "Couldn't get {} from option {:s} = '{:s}': {}" -msgstr "" +msgstr "Impossible d'obtenir {} à partir de l'option {:s} = '{:s}' : {}" -#: ../src/bout++.cxx:508 -#, fuzzy, c++-format +#: ../src/bout++.cxx:515 +#, c++-format msgid "DataDir \"{:s}\" does not exist or is not accessible\n" msgstr "" "Le répertoire de données \"{:s}\" n'existe pas ou n'est pas accessible\n" -#: ../src/bout++.cxx:505 -#, fuzzy, c++-format +#: ../src/bout++.cxx:512 +#, c++-format msgid "DataDir \"{:s}\" is not a directory\n" -msgstr "\"{:s}\" n'est pas un répertoire\n" +msgstr "" +"Le répertoire de données \"{:s}\" n'est pas un répertoire\n" -#: ../src/solver/solver.cxx:665 +#: ../src/solver/solver.cxx:671 msgid "ERROR: Solver is already initialised\n" msgstr "" +"ERREUR : le solveur est déjà initialisé\n" -#: ../src/bout++.cxx:209 -#, fuzzy, c++-format +#: ../src/bout++.cxx:216 +#, c++-format msgid "Error encountered during initialisation: {:s}\n" -msgstr "Erreur rencontrée lors de l'initialisation : {:s}\n" +msgstr "" +"Erreur lors de l'initialisation : {:s}\n" -#: ../src/bout++.cxx:744 +#: ../src/bout++.cxx:751 msgid "Error whilst writing settings" -msgstr "" +msgstr "Erreur lors de l'écriture des paramètres" -#: ../src/mesh/impls/bout/boutmesh.cxx:332 +#: ../src/mesh/impls/bout/boutmesh.cxx:323 #, c++-format msgid "Error: nx must be greater than 2 times MXG (2 * {:d})" -msgstr "" +msgstr "Erreur : nx doit être supérieur à 2 fois MXG (2 * {:d})" -#: ../src/solver/solver.cxx:512 +#: ../src/solver/solver.cxx:520 msgid "Failed to initialise solver-> Aborting\n" -msgstr "Échec d'initialisation du solutionneur -> Abandonner\n" +msgstr "" +"Échec d'initialisation du solutionneur -> Abandonner\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:290 +#: ../src/mesh/impls/bout/boutmesh.cxx:281 #, c++-format msgid "Finding value for NXPE (ideal = {:f})\n" msgstr "" +"Recherche d'une valeur pour NXPE (idéal = {:f})\n" -#: ../src/solver/solver.cxx:668 +#: ../src/solver/solver.cxx:674 msgid "Initialising solver\n" msgstr "" +"Initialisation du solveur\n" -#: ../src/bout++.cxx:494 +#: ../src/bout++.cxx:501 msgid "" "Input and output file for settings must be different.\n" "Provide -o to avoid this issue.\n" msgstr "" +"Les fichiers d'entrée et de sortie des paramètres doivent être différents.\n" +"Fournissez -o pour éviter ce problème.\n" #: ../src/sys/optionsreader.cxx:76 msgid "Invalid command line option '-' found - maybe check whitespace?" -msgstr "" +msgstr "Option de ligne de commande '-' invalide trouvée ; vérifiez peut-être les espaces" -#: ../src/mesh/impls/bout/boutmesh.cxx:400 +#: ../src/mesh/impls/bout/boutmesh.cxx:436 msgid "Loading mesh" -msgstr "" +msgstr "Chargement de la grille" -#: ../src/mesh/impls/bout/boutmesh.cxx:415 +#: ../src/mesh/impls/bout/boutmesh.cxx:451 msgid "Mesh must contain nx" -msgstr "" +msgstr "La grille doit contenir nx" -#: ../src/mesh/impls/bout/boutmesh.cxx:419 +#: ../src/mesh/impls/bout/boutmesh.cxx:455 msgid "Mesh must contain ny" -msgstr "" +msgstr "La grille doit contenir ny" #. Not found -#: ../src/mesh/mesh.cxx:536 +#: ../src/mesh/mesh.cxx:547 #, c++-format msgid "Missing integer array {:s}\n" msgstr "" +"Tableau d'entiers manquant {:s}\n" -#: ../src/solver/solver.cxx:905 +#: ../src/solver/solver.cxx:911 #, c++-format msgid "Monitor signalled to quit (exception {})\n" msgstr "" +"Le moniteur a signalé l'arrêt (exception {})\n" -#: ../src/solver/solver.cxx:883 +#: ../src/solver/solver.cxx:889 #, c++-format msgid "Monitor signalled to quit (return code {})" -msgstr "" +msgstr "Le moniteur a signalé l'arrêt (code de retour {})" -#: ../src/bout++.cxx:823 +#: ../src/bout++.cxx:834 msgid "Name of file whose existence triggers a stop" -msgstr "" +msgstr "Nom du fichier dont l'existence déclenche un arrêt" -#: ../src/mesh/impls/bout/boutmesh.cxx:565 +#: ../src/mesh/impls/bout/boutmesh.cxx:601 msgid "No boundary regions in this processor" -msgstr "" +msgstr "Aucune région de bord dans ce processeur" -#: ../src/mesh/impls/bout/boutmesh.cxx:550 +#: ../src/mesh/impls/bout/boutmesh.cxx:586 msgid "No boundary regions; domain is periodic\n" msgstr "" +"Aucune région de bord ; le domaine est périodique\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:254 +#: ../src/mesh/impls/bout/boutmesh.cxx:245 #, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n" msgstr "" +"Le nombre de processeurs ({:d}) n'est pas divisible par les NP dans la direction x ({:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:267 +#: ../src/mesh/impls/bout/boutmesh.cxx:258 #, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n" msgstr "" +"Le nombre de processeurs ({:d}) n'est pas divisible par les NP dans la direction y ({:d})\n" #. Less than 2 time-steps left -#: ../src/bout++.cxx:896 +#: ../src/bout++.cxx:908 #, c++-format msgid "Only {:e} seconds ({:.2f} steps) left. Quitting\n" msgstr "" +"Il ne reste que {:e} secondes ({:.2f} pas). Arrêt\n" -#: ../src/sys/options.cxx:303 ../src/sys/options.cxx:345 -#: ../src/sys/options.cxx:393 ../src/sys/options.cxx:428 -#: ../src/sys/options.cxx:703 ../src/sys/options.cxx:730 -#: ../src/sys/options.cxx:757 +#: ../src/sys/options.cxx:382 ../src/sys/options.cxx:398 +#: ../src/sys/options.cxx:441 ../src/sys/options.cxx:471 +#: ../src/sys/options.cxx:745 ../src/sys/options.cxx:767 +#: ../src/sys/options.cxx:789 #, c++-format msgid "Option {:s} has no value" -msgstr "" +msgstr "L'option {:s} n'a pas de valeur" #. Doesn't exist -#: ../src/sys/options.cxx:159 +#: ../src/sys/options.cxx:172 #, c++-format msgid "Option {:s}:{:s} does not exist" -msgstr "" +msgstr "L'option {:s}:{:s} n'existe pas" -#: ../include/bout/options.hxx:828 +#: ../include/bout/options.hxx:904 #, c++-format msgid "" "Options: Setting a value from same source ({:s}) to new value '{:s}' - old " "value was '{:s}'." -msgstr "" +msgstr "Options : définition d'une valeur depuis la même source ({:s}) vers la nouvelle valeur '{:s}' - l'ancienne valeur était '{:s}'." -#: ../src/mesh/impls/bout/boutmesh.cxx:552 +#: ../src/mesh/impls/bout/boutmesh.cxx:588 msgid "Possible boundary regions are: " -msgstr "" +msgstr "Les régions de bord possibles sont : " -#: ../src/bout++.cxx:538 +#: ../src/bout++.cxx:545 #, c++-format msgid "" "Processor number: {:d} of {:d}\n" "\n" msgstr "" +"Processeur numéro {:d} sur {:d}\n" +"\n" -#: ../src/mesh/mesh.cxx:609 +#: ../src/mesh/mesh.cxx:642 #, c++-format msgid "Registered region 2D {:s}" -msgstr "" +msgstr "Région 2D enregistrée {:s}" -#: ../src/mesh/mesh.cxx:599 +#: ../src/mesh/mesh.cxx:632 #, c++-format msgid "Registered region 3D {:s}" -msgstr "" +msgstr "Région 3D enregistrée {:s}" -#: ../src/mesh/mesh.cxx:619 +#: ../src/mesh/mesh.cxx:652 #, c++-format msgid "Registered region Perp {:s}" -msgstr "" +msgstr "Région Perp enregistrée {:s}" -#: ../src/bout++.cxx:529 +#: ../src/bout++.cxx:536 #, c++-format msgid "Revision: {:s}\n" msgstr "" +"Révision : {:s}\n" -#: ../src/solver/solver.cxx:581 +#: ../src/solver/solver.cxx:587 msgid "Run time : " msgstr "Temps d'exécution : " #. / Run the solver -#: ../src/solver/solver.cxx:525 +#: ../src/solver/solver.cxx:533 msgid "" "Running simulation\n" "\n" @@ -716,68 +851,73 @@ msgstr "" #: ../tests/unit/src/test_bout++.cxx:359 msgid "Signal" -msgstr "" +msgstr "Signal" -#: ../src/bout++.cxx:865 +#: ../src/bout++.cxx:876 msgid "" "Sim Time | RHS evals | Wall Time | Calc Inv Comm I/O SOLVER\n" "\n" msgstr "" +"Temps sim. | Eval. RHS | Temps mur | Calc Inv Comm E/S SOLVEUR\n" +"\n" -#: ../src/bout++.cxx:868 +#: ../src/bout++.cxx:879 msgid "" "Sim Time | RHS_e evals | RHS_I evals | Wall Time | Calc Inv " "Comm I/O SOLVER\n" "\n" msgstr "" +"Temps sim. | Eval. RHS_e | Eval. RHS_I | Temps mur | Calc Inv Comm E/S SOLVEUR\n" +"\n" -#: ../src/solver/solver.cxx:506 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:514 +#, c++-format msgid "Solver running for {:d} outputs with monitor timestep of {:e}\n" msgstr "" -"Le solveur fonctionne pour {:d} sorties avec un temps de moniteur de {:e}\n" +"Le solveur s'exécute pour {:d} sorties avec un pas de temps de moniteur de {:e}\n" -#: ../src/solver/solver.cxx:502 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:510 +#, c++-format msgid "Solver running for {:d} outputs with output timestep of {:e}\n" msgstr "" -"Le solveur fonctionne pour {:d} sorties avec un pas de sortie de {:e}\n" +"Le solveur s'exécute pour {:d} sorties avec un pas de temps de sortie de {:e}\n" -#: ../src/solver/solver.cxx:781 +#: ../src/solver/solver.cxx:788 #, c++-format msgid "" "Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) after init is " "called!" -msgstr "" +msgstr "Solver::addMonitor : impossible de réduire le pas de temps (de {:g} à {:g}) après l'appel à init !" -#: ../src/solver/solver.cxx:1281 +#: ../src/solver/solver.cxx:1289 #, c++-format msgid "" "Time derivative at wrong location - Field is at {:s}, derivative is at {:s} " "for field '{:s}'\n" msgstr "" +"Dérivée temporelle au mauvais emplacement - le champ est à {:s}, la dérivée est à {:s} pour le champ '{:s}'\n" -#: ../src/solver/solver.cxx:1480 +#: ../src/solver/solver.cxx:1494 #, c++-format msgid "Time derivative for variable '{:s}' not set" -msgstr "" +msgstr "La dérivée temporelle de la variable '{:s}' n'est pas définie" -#: ../src/mesh/mesh.cxx:605 +#: ../src/mesh/mesh.cxx:638 #, c++-format msgid "Trying to add an already existing region {:s} to regionMap2D" -msgstr "" +msgstr "Tentative d'ajout d'une région déjà existante {:s} à regionMap2D" -#: ../src/mesh/mesh.cxx:595 +#: ../src/mesh/mesh.cxx:614 #, c++-format msgid "Trying to add an already existing region {:s} to regionMap3D" -msgstr "" +msgstr "Tentative d'ajout d'une région déjà existante {:s} à regionMap3D" -#: ../src/mesh/mesh.cxx:616 +#: ../src/mesh/mesh.cxx:649 #, c++-format msgid "Trying to add an already existing region {:s} to regionMapPerp" -msgstr "" +msgstr "Tentative d'ajout d'une région déjà existante {:s} à regionMapPerp" -#: ../src/sys/options.cxx:99 ../src/sys/options.cxx:138 +#: ../src/sys/options.cxx:112 ../src/sys/options.cxx:151 #, c++-format msgid "" "Trying to index Option '{0}' with '{1}', but '{0}' is a value, not a " @@ -785,156 +925,179 @@ msgid "" "This is likely the result of clashing input options, and you may have to " "rename one of them.\n" msgstr "" +"Tentative d'indexer l'option '{0}' avec '{1}', mais '{0}' est une valeur, pas une section.\n" +"Ceci est probablement dû à des options d'entrée en conflit, et vous devrez peut-être renommer l'une d'elles.\n" -#: ../src/mesh/coordinates.cxx:1462 +#: ../src/mesh/coordinates.cxx:1464 msgid "" "Unrecognised paralleltransform option.\n" "Valid choices are 'identity', 'shifted', 'fci'" msgstr "" +"Option paralleltransform non reconnue.\n" +"Les choix valides sont 'identity', 'shifted', 'fci'" -#: ../src/sys/options.cxx:872 +#: ../src/sys/options.cxx:899 msgid "Unused options:\n" msgstr "" +"Options inutilisées :\n" -#: ../src/bout++.cxx:439 +#: ../src/bout++.cxx:446 #, c++-format msgid "Usage is {:s} -d \n" msgstr "" +"Utilisation : {:s} -d \n" -#: ../src/bout++.cxx:448 +#: ../src/bout++.cxx:455 #, c++-format msgid "Usage is {:s} -f \n" msgstr "" +"Utilisation : {:s} -f \n" -#: ../src/bout++.cxx:466 +#: ../src/bout++.cxx:473 #, c++-format msgid "Usage is {:s} -l \n" msgstr "" +"Utilisation : {:s} -l \n" -#: ../src/bout++.cxx:457 +#: ../src/bout++.cxx:464 #, c++-format msgid "Usage is {:s} -o \n" msgstr "" +"Utilisation : {:s} -o \n" -#: ../src/bout++.cxx:353 +#: ../src/bout++.cxx:360 #, c++-format msgid "Usage is {} {} \n" msgstr "" +"Utilisation : {} {} \n" #: ../tests/unit/src/test_bout++.cxx:32 ../tests/unit/src/test_bout++.cxx:46 msgid "Usage:" -msgstr "" +msgstr "Utilisation :" #. Print help message -- note this will be displayed once per processor as we've not #. started MPI yet. -#: ../src/bout++.cxx:367 +#: ../src/bout++.cxx:374 #, c++-format msgid "" "Usage: {:s} [-d ] [-f ] [restart [append]] " "[VAR=VALUE]\n" msgstr "" +"Utilisation : {:s} [-d ] [-f ] [restart [append]] [VAR=VALUE]\n" #. restart file should be written by physics model -#: ../src/solver/solver.cxx:921 +#: ../src/solver/solver.cxx:927 msgid "User signalled to quit. Returning\n" msgstr "" +"L'utilisateur a demandé l'arrêt. Retour\n" + +#: ../src/sys/options.cxx:486 +#, c++-format +msgid "Value for option {:s} = {:e} is not a bool" +msgstr "La valeur de l'option {:s} = {:e} n'est pas un booléen" -#: ../src/sys/options.cxx:373 +#: ../src/sys/options.cxx:426 #, c++-format msgid "Value for option {:s} = {:e} is not an integer" -msgstr "" +msgstr "La valeur de l'option {:s} = {:e} n'est pas un entier" -#: ../src/sys/options.cxx:408 +#: ../src/sys/options.cxx:456 #, c++-format msgid "Value for option {:s} cannot be converted to a BoutReal" -msgstr "" +msgstr "La valeur de l'option {:s} ne peut pas être convertie en BoutReal" -#: ../src/sys/options.cxx:581 +#: ../src/sys/options.cxx:623 #, c++-format msgid "Value for option {:s} cannot be converted to a Field2D" -msgstr "" +msgstr "La valeur de l'option {:s} ne peut pas être convertie en Field2D" -#: ../src/sys/options.cxx:529 +#: ../src/sys/options.cxx:571 #, c++-format msgid "Value for option {:s} cannot be converted to a Field3D" -msgstr "" +msgstr "La valeur de l'option {:s} ne peut pas être convertie en Field3D" -#: ../src/sys/options.cxx:663 +#: ../src/sys/options.cxx:705 #, c++-format msgid "Value for option {:s} cannot be converted to a FieldPerp" -msgstr "" +msgstr "La valeur de l'option {:s} ne peut pas être convertie en FieldPerp" -#: ../src/sys/options.cxx:451 +#: ../src/sys/options.cxx:491 #, c++-format msgid "Value for option {:s} cannot be converted to a bool" -msgstr "" +msgstr "La valeur de l'option {:s} ne peut pas être convertie en bool" -#: ../src/sys/options.cxx:709 +#: ../src/sys/options.cxx:751 #, c++-format msgid "Value for option {:s} cannot be converted to an Array" -msgstr "" +msgstr "La valeur de l'option {:s} ne peut pas être convertie en Array" -#: ../src/sys/options.cxx:736 +#: ../src/sys/options.cxx:773 #, c++-format msgid "Value for option {:s} cannot be converted to an Matrix" -msgstr "" +msgstr "La valeur de l'option {:s} ne peut pas être convertie en Matrix" -#: ../src/sys/options.cxx:763 +#: ../src/sys/options.cxx:795 #, c++-format msgid "Value for option {:s} cannot be converted to an Tensor" -msgstr "" +msgstr "La valeur de l'option {:s} ne peut pas être convertie en Tensor" #. Another type which can't be converted -#: ../src/sys/options.cxx:365 +#: ../src/sys/options.cxx:418 #, c++-format msgid "Value for option {:s} is not an integer" -msgstr "" +msgstr "La valeur de l'option {:s} n'est pas un entier" -#: ../src/solver/solver.cxx:1232 ../src/solver/solver.cxx:1238 +#: ../src/solver/solver.cxx:1240 ../src/solver/solver.cxx:1246 #, c++-format msgid "Variable '{:s}' not initialised" -msgstr "" +msgstr "La variable '{:s}' n'est pas initialisée" -#: ../src/mesh/impls/bout/boutmesh.cxx:431 +#: ../src/mesh/impls/bout/boutmesh.cxx:467 #, c++-format msgid "" "WARNING: Number of toroidal points should be 2^n for efficient FFT " "performance -- consider changing MZ ({:d}) if using FFTs\n" msgstr "" +"AVERTISSEMENT : le nombre de points toroïdaux devrait être 2^n pour de bonnes performances FFT ; envisagez de modifier MZ ({:d}) si vous utilisez des FFT\n" -#: ../src/mesh/coordinates.cxx:633 +#: ../src/mesh/coordinates.cxx:635 msgid "WARNING: extrapolating input mesh quantities into x-boundary cells\n" msgstr "" +"AVERTISSEMENT : extrapolation des quantités de la grille d'entrée dans les cellules de bord x\n" -#: ../src/mesh/coordinates.cxx:410 +#: ../src/mesh/coordinates.cxx:412 msgid "" "WARNING: extrapolating input mesh quantities into x-boundary cells. Set " "option extrapolate_x=false to disable this.\n" msgstr "" +"AVERTISSEMENT : extrapolation des quantités de la grille d'entrée dans les cellules de bord x. Définissez l'option extrapolate_x=false pour désactiver cela.\n" -#: ../src/mesh/coordinates.cxx:638 +#: ../src/mesh/coordinates.cxx:640 msgid "WARNING: extrapolating input mesh quantities into y-boundary cells\n" msgstr "" +"AVERTISSEMENT : extrapolation des quantités de la grille d'entrée dans les cellules de bord y\n" -#: ../src/mesh/coordinates.cxx:415 +#: ../src/mesh/coordinates.cxx:417 msgid "" "WARNING: extrapolating input mesh quantities into y-boundary cells. Set " "option extrapolate_y=false to disable this.\n" msgstr "" +"AVERTISSEMENT : extrapolation des quantités de la grille d'entrée dans les cellules de bord y. Définissez l'option extrapolate_y=false pour désactiver cela.\n" -#: ../src/bout++.cxx:814 +#: ../src/bout++.cxx:825 msgid "Wall time limit in hours. By default (< 0), no limit" -msgstr "" +msgstr "Limite de temps mur en heures. Par défaut (< 0), aucune limite" #: ../src/sys/optionsreader.cxx:42 #, c++-format msgid "Writing options to file {:s}\n" msgstr "" +"Écriture des options dans le fichier {:s}\n" #. / The source label given to default values -#: ../src/sys/options.cxx:15 +#: ../src/sys/options.cxx:34 msgid "default" -msgstr "" +msgstr "par défaut" #~ msgid "\tChecking disabled\n" #~ msgstr "\tVérification désactivée\n" @@ -946,10 +1109,6 @@ msgstr "" #~ msgid "\tSignal handling enabled\n" #~ msgstr "\tTraitement du signal activé\n" -#, fuzzy -#~ msgid "Option {:s} is not a section" -#~ msgstr "\"{:s}\" n'est pas un répertoire\n" - #, fuzzy #~ msgid "Error encountered during initialisation\n" #~ msgstr "Erreur rencontrée lors de l'initialisation\n" diff --git a/locale/ka/libbout.po b/locale/ka/libbout.po new file mode 100644 index 0000000000..70ed52aafb --- /dev/null +++ b/locale/ka/libbout.po @@ -0,0 +1,1084 @@ +# Georgian translation for libbout. +# Copyright (C) 2026 libbout's authors. +# This file is distributed under the same license as the libbout package. +# Ekaterine Papava , 2026. +# +msgid "" +msgstr "" +"Project-Id-Version: libbout\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-08-13 23:37+0100\n" +"PO-Revision-Date: 2026-03-23 08:06+0100\n" +"Last-Translator: Ekaterine Papava \n" +"Language-Team: Georgian <(nothing)>\n" +"Language: ka\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.9\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:182 +#, c++-format +msgid "" +"\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " +"of MYSUB ({:d})\n" +msgstr "" +"\t -> რეგიონი Core jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) MYSUB-ის ({:d}) " +"ნამრავლი უნდა იყოს\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:215 +#, c++-format +msgid "" +"\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " +"of MYSUB ({:d})\n" +msgstr "" +"\t -> რეგიონი Core jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) MYSUB-ის ({:d}) " +"ნამრავლი უნდა იყოს\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:190 +#, c++-format +msgid "" +"\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must be a multiple " +"of MYSUB ({:d})\n" +msgstr "" +"\t -> რეგიონი Core jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) MYSUB-ის ({:d}) " +"ნამრავლი უნდა იყოს\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:300 +msgid "\t -> Good value\n" +msgstr "\t -> კარგი მნიშვნელობა\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:171 +#, c++-format +msgid "" +"\t -> Leg region jyseps1_1+1 ({:d}) must be a multiple of MYSUB ({:d})\n" +msgstr "\t -> რეგიონი Leg jyseps1_1+1 ({:d}) MYSUB-ის ({:d}) ნამრავლი უნდა იყოს\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:206 +#, c++-format +msgid "" +"\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must be a " +"multiple of MYSUB ({:d})\n" +msgstr "" +"\t -> რეგიონი leg jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) MYSUB-ის ({:d}) " +"ნამრავლი უნდა იყოს\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:223 +#, c++-format +msgid "" +"\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a multiple of " +"MYSUB ({:d})\n" +msgstr "" +"\t -> რეგიონი leg ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) MYSUB-ის ({:d}) " +"ნამრავლი უნდა იყოს\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:199 +#, c++-format +msgid "" +"\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must be a " +"multiple of MYSUB ({:d})\n" +msgstr "" +"\t -> რეგიონი leg ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) MYSUB-ის ({:d}) " +"ნამრავლი უნდა იყოს\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:166 +#, c++-format +msgid "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n" +msgstr "\t -> ny/NYPE ({:d}/{:d} = {:d}) უნდა იყოს >= MYG ({:d})\n" + +#: ../src/bout++.cxx:575 +#, c++-format +msgid "\tADIOS2 support {}\n" +msgstr "\tADIOS2-ის მხარდაჭერა {}\n" + +#: ../src/bout++.cxx:583 +#, c++-format +msgid "\tBacktrace in exceptions {}\n" +msgstr "\tუკუტრეისი გამონაკლისებში {}\n" + +#. Loop over all possibilities +#. Processors divide equally +#. Mesh in X divides equally +#. Mesh in Y divides equally +#: ../src/mesh/impls/bout/boutmesh.cxx:288 +#, c++-format +msgid "\tCandidate value: {:d}\n" +msgstr "\tკანდიდატი მნიშვნელობა: {:d}\n" + +#: ../src/bout++.cxx:584 +#, c++-format +msgid "\tColour in logs {}\n" +msgstr "\tფერები ჟურნალში {}\n" + +#: ../src/bout++.cxx:599 +msgid "\tCommand line options for this run : " +msgstr "\tბრძანების სტრიქონის პარამეტრები ამ გაშვებისთვის : " + +#. The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings +#. which could cause problems (e.g. terminate strings). +#: ../src/bout++.cxx:595 +#, c++-format +msgid "\tCompiled with flags : {:s}\n" +msgstr "\tდაკომპილებულია ალმებით : {:s}\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:315 +#, c++-format +msgid "" +"\tDomain split (NXPE={:d}, NYPE={:d}) into domains (localNx={:d}, " +"localNy={:d})\n" +msgstr "" +"\tდომენი დაყოფილია (NXPE={:d}, NYPE={:d}) დომენებად (localNx={:d}, " +"localNy={:d})\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:357 +#, c++-format +msgid "\tERROR: Cannot split {:d} Y points equally between {:d} processors\n" +msgstr "" +"\tშეცდომა: {:d} Y წერტილის თანაბრად განაწილება {:d} პროცესორს შორის " +"შეუძლებელია\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:365 +#, c++-format +msgid "\tERROR: Cannot split {:d} Z points equally between {:d} processors\n" +msgstr "" +"\tშეცდომა: {:d} Z წერტილის თანაბრად განაწილება {:d} პროცესორს შორის " +"შეუძლებელია\n" + +#: ../src/sys/options/options_ini.cxx:200 +#, c++-format +msgid "" +"\tEmpty key\n" +"\tLine: {:s}" +msgstr "" +"\tცარიელი გასაღები\n" +"\tხაზი: {:s}" + +#: ../src/sys/optionsreader.cxx:127 +#, c++-format +msgid "\tEmpty key or value in command line '{:s}'\n" +msgstr "\tცარიელი გასაღები, ან მნიშვნელობა ბრძანების სტრიქონში '{:s}'\n" + +#: ../src/bout++.cxx:587 +#, c++-format +msgid "\tExtra debug output {}\n" +msgstr "\tგამართვის დამატებითი შეტყობინებები {}\n" + +#: ../src/bout++.cxx:568 +#, c++-format +msgid "\tFFT support {}\n" +msgstr "\tFFT-ის მხარდაჭერა {}\n" + +#: ../src/bout++.cxx:590 +#, c++-format +msgid "\tField name tracking {}\n" +msgstr "\tველის სახელის თვალყურის დევნება {}\n" + +#: ../src/bout++.cxx:588 +#, c++-format +msgid "\tFloating-point exceptions {}\n" +msgstr "\tმცურავმძიმიანი გამონაკლისები {}\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:476 +msgid "\tGrid size: " +msgstr "\tბადის ზომა: " + +#: ../src/mesh/impls/bout/boutmesh.cxx:499 +msgid "\tGuard cells (x,y,z): " +msgstr "\tუჯრედების დაცვა (x,y,z): " + +#: ../src/sys/options/options_ini.cxx:204 +#, c++-format +msgid "" +"\tKey must not contain ':' character\n" +"\tLine: {:s}" +msgstr "" +"\tგასაღები არ უნდა შეიცავდეს სიმბოლოს ':'\n" +"\tხაზი: {:s}" + +#: ../src/bout++.cxx:570 +#, c++-format +msgid "\tLAPACK support {}\n" +msgstr "\tLAPACK-ის მხარდაჭერა {}\n" + +#: ../src/bout++.cxx:591 +#, c++-format +msgid "\tMessage stack {}\n" +msgstr "\tშეტყობინებების სტეკი {}\n" + +#: ../src/bout++.cxx:567 +#, c++-format +msgid "\tMetrics mode is {}\n" +msgstr "\tმეტრიკების რეჟიმია {}\n" + +#: ../src/sys/optionsreader.cxx:111 +#, c++-format +msgid "\tMultiple '=' in command-line argument '{:s}'\n" +msgstr "\tერთზე მეტი '=' ბრძანების სტრიქონის არგუმენტში '{:s}'\n" + +#: ../src/bout++.cxx:569 +#, c++-format +msgid "\tNatural language support {}\n" +msgstr "\tნატურალური ენის მხარდაჭერა {}\n" + +#: ../src/bout++.cxx:574 +#, c++-format +msgid "\tNetCDF support {}{}\n" +msgstr "\tNetCDF-ის მხარდაჭერა {}{}\n" + +#: ../src/bout++.cxx:585 +#, c++-format +msgid "\tOpenMP parallelisation {}, using {} threads\n" +msgstr "\tOpenMP-ის პარალელიზაცია {}. გამოიყენება {} ნაკადი\n" + +#. Mark the option as used +#. Option not found +#: ../include/bout/options.hxx:586 ../include/bout/options.hxx:619 +#: ../include/bout/options.hxx:643 ../include/bout/options.hxx:896 +msgid "\tOption " +msgstr "\tპარამეტრი " + +#: ../src/sys/options.cxx:369 +#, c++-format +msgid "\tOption {} = {}" +msgstr "\tპარამეტრი {} = {}" + +#: ../src/sys/options/options_ini.cxx:70 +#, c++-format +msgid "\tOptions file '{:s}' not found\n" +msgstr "\tპარამეტრების ფაილი '{:s}' აღმოჩენილი არაა\n" + +#: ../src/bout++.cxx:576 +#, c++-format +msgid "\tPETSc support {}\n" +msgstr "\tPETSc-ის მხარდაჭერა {}\n" + +#: ../src/bout++.cxx:579 +#, c++-format +msgid "\tPVODE support {}\n" +msgstr "\tPVODE-ის მხარდაჭერა {}\n" + +#: ../src/bout++.cxx:564 +msgid "\tParallel NetCDF support disabled\n" +msgstr "\tპარალელური NetCDF-ის მხარდაჭერა გათიშულია\n" + +#: ../src/bout++.cxx:562 +msgid "\tParallel NetCDF support enabled\n" +msgstr "\tპარალელური NetCDF-ის მხარდაჭერა ჩართულია\n" + +#: ../src/bout++.cxx:577 +#, c++-format +msgid "\tPretty function name support {}\n" +msgstr "\tლამაზი ფუნქციის სახელის მხარდაჭერა {}\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:473 +msgid "\tRead nz from input grid file\n" +msgstr "\tnz წაკითხულია შეყვანილი ბადის ფაილიდან\n" + +#: ../src/mesh/mesh.cxx:249 +msgid "\tReading contravariant vector " +msgstr "\tკონტრავარიანტული ვექტორის წაკითხვა " + +#: ../src/mesh/mesh.cxx:242 ../src/mesh/mesh.cxx:263 +msgid "\tReading covariant vector " +msgstr "\tკოვარიანტული ვექტორის წაკითხვა " + +#: ../src/bout++.cxx:555 +#, c++-format +msgid "\tRuntime error checking {}" +msgstr "\tგაშვების დროის შემოწმება {}" + +#: ../src/bout++.cxx:581 +#, c++-format +msgid "\tSLEPc support {}\n" +msgstr "\tSLEPc-ის მხარდაჭერა {}\n" + +#: ../src/bout++.cxx:582 +#, c++-format +msgid "\tSUNDIALS support {}\n" +msgstr "\tSUNDIALS-ის მხარდაჭერა {}\n" + +#: ../src/bout++.cxx:580 +#, c++-format +msgid "\tScore-P support {}\n" +msgstr "\tScore-P-ის მხარდაჭერა {}\n" + +#: ../src/bout++.cxx:589 +#, c++-format +msgid "\tSignal handling support {}\n" +msgstr "\tსიგნალის დამუშავების მხარდაჭერა {}\n" + +#: ../src/solver/impls/split-rk/split-rk.cxx:76 +#, c++-format +msgid "\tUsing a timestep {:e}\n" +msgstr "\tგამოიყენება დროის შტამპი {:e}\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:613 +msgid "\tdone\n" +msgstr "\tმზადაა\n" + +#: ../src/solver/impls/split-rk/split-rk.cxx:41 +msgid "" +"\n" +"\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n" +msgstr "" +"\n" +"\tგაყოფილი Runge-Kutta-Legendre და SSP-RK3 ამომხსნელი\n" + +#: ../src/bout++.cxx:378 +msgid "" +"\n" +" -d \t\tLook in for input/output files\n" +" -f \t\tUse OPTIONS given in \n" +" -o \tSave used OPTIONS given to \n" +" -l, --log \tPrint log to \n" +" -v, --verbose\t\t\tIncrease verbosity\n" +" -q, --quiet\t\t\tDecrease verbosity\n" +msgstr "" +"\n" +" -d <მონაცემთა_საქაღალდე>\t\tშეტანა/გამოტანის ფაილების ძებნა " +"<მონაცემთა_საქაღალდეში>\n" +" -f <პარამეტრების_ფაილის_სახელი>\t\t<პარამეტრების_ფაილში> მითითებული " +"პარამეტრების გამოყენება\n" +" -o <პარამეტრების_ფაილი>\tგადაცემული პარამეტრების შენახვა " +"<პარამეტრების_ფაილში>\n" +" -l, --log <ჟურნალის_ფაილის_სახელი>\tჟურნალის ჩაწერა " +"<ჟურნალის_ფაილის_სახელში>\n" +" -v, --verbose\t\t\tმეტი შეტყობინების გამოტანა\n" +" -q, --quiet\t\t\tნაკლები შეტყობინების გამოტანა\n" + +#: ../src/sys/expressionparser.cxx:341 +#, c++-format +msgid "" +"\n" +" {1: ^{2}}{0}\n" +" Did you mean '{0}'?" +msgstr "" +"\n" +" {1: ^{2}}{0}\n" +" გულისხმობდით '{0}'-ს?" + +#: ../src/solver/solver.cxx:586 +#, c++-format +msgid "" +"\n" +"Run finished at : {:s}\n" +msgstr "" +"\n" +"დასრულების დროა : {:s}\n" + +#: ../src/solver/solver.cxx:540 +#, c++-format +msgid "" +"\n" +"Run started at : {:s}\n" +msgstr "" +"\n" +"გაშვების დროა : {:s}\n" + +#. Raw string to help with the formatting of the message, and a +#. separate variable so clang-format doesn't barf on the +#. exception +#: ../src/sys/options.cxx:1158 +msgid "" +"\n" +"There were unused input options:\n" +"-----\n" +"{:i}\n" +"-----\n" +"It's possible you've mistyped some options. BOUT++ input arguments are\n" +"now case-sensitive, and some have changed name. You can try running\n" +"\n" +" /bin/bout-v5-input-file-upgrader.py {}/{}\n" +"\n" +"to automatically fix the most common issues. If these options above\n" +"are sometimes used depending on other options, you can call\n" +"`Options::setConditionallyUsed()`, for example:\n" +"\n" +" Options::root()[\"{}\"].setConditionallyUsed();\n" +"\n" +"to mark a section or value as depending on other values, and so ignore\n" +"it in this check. Alternatively, if you're sure the above inputs are\n" +"not a mistake, you can set 'input:error_on_unused_options=false' to\n" +"turn off this check for unused options. You can always set\n" +"'input:validate=true' to check inputs without running the full\n" +"simulation.\n" +"\n" +"{}" +msgstr "" +"\n" +"აღმოჩენილია გამოუყენებელი შეყვანის პარამეტრები:\n" +"-----\n" +"{:i}\n" +"-----\n" +"შესაძლებელია, ზოგიერთი პარამეტრი შეცდომით გაქვთ აკრეფილი. BOUT++-ის " +"შეყვანის არგუმენტები\n" +"ახლა დიდ და პატარა ასოებს არჩევს და ზოგიერთ მათგანს სახელიც შეეცვალა. " +"შეგიძლიათ გაუშვათ\n" +"\n" +" /bin/bout-v5-input-file-upgrader.py {}/{}\n" +"\n" +"რათა ყველაზე გავრცელებული პრობლემები ავტომატურად გასწორდეს. თუ ზემოთ მოცემული " +"პარამეტრები\n" +"ზოგჯერ სხვა პარამეტრებზეა დამოკიდებული, შეგიძლიათ გამოიძახოთ\n" +"`Options::setConditionallyUsed()`, მაგალითად:\n" +"\n" +" Options::root()[\"{}\"].setConditionallyUsed();\n" +"\n" +"რათა მონაკვეთი ან მნიშვნელობა სხვა მნიშვნელობებზე დამოკიდებულად მონიშნოთ და ამ " +"შემოწმებაში\n" +"მისი იგნორირება მოხდეს. თუ დარწმუნებული ხართ, რომ ზემოთ მითითებული შეყვანები\n" +"შეცდომა არაა, შეგიძლიათ დააყენოთ 'input:error_on_unused_options=false', რათა\n" +"გამოუყენებელი პარამეტრების ეს შემოწმება გამოირთოს. ასევე ყოველთვის შეგიძლიათ\n" +"'input:validate=true' დააყენოთ, რათა შეყვანები სრული სიმულაციის გაშვების " +"გარეშე გადაამოწმოთ.\n" +"\n" +"{}" + +#: ../src/bout++.cxx:389 +#, c++-format +msgid "" +" --print-config\t\tPrint the compile-time configuration\n" +" --list-solvers\t\tList the available time solvers\n" +" --help-solver \tPrint help for the given time solver\n" +" --list-laplacians\t\tList the available Laplacian inversion solvers\n" +" --help-laplacian \tPrint help for the given Laplacian inversion " +"solver\n" +" --list-laplacexz\t\tList the available LaplaceXZ inversion solvers\n" +" --help-laplacexz \tPrint help for the given LaplaceXZ inversion " +"solver\n" +" --list-invertpars\t\tList the available InvertPar solvers\n" +" --help-invertpar \tPrint help for the given InvertPar solver\n" +" --list-rkschemes\t\tList the available Runge-Kutta schemes\n" +" --help-rkscheme \tPrint help for the given Runge-Kutta scheme\n" +" --list-meshes\t\t\tList the available Meshes\n" +" --help-mesh \t\tPrint help for the given Mesh\n" +" --list-xzinterpolations\tList the available XZInterpolations\n" +" --help-xzinterpolation \tPrint help for the given " +"XZInterpolation\n" +" --list-zinterpolations\tList the available ZInterpolations\n" +" --help-zinterpolation \tPrint help for the given " +"ZInterpolation\n" +" -h, --help\t\t\tThis message\n" +" restart [append]\t\tRestart the simulation. If append is specified, append " +"to the existing output files, otherwise overwrite them\n" +" VAR=VALUE\t\t\tSpecify a VALUE for input parameter VAR\n" +"\n" +"For all possible input parameters, see the user manual and/or the physics " +"model source (e.g. {:s}.cxx)\n" +msgstr "" +" --print-config\t\tკომპილაციის დროის კონფიგურაციის გამოტანა\n" +" --list-solvers\t\tხელმისაწვდომი დროის ამომხსნელების სია\n" +" --help-solver \tდახმარების გამოტანა მოცემული დროის ამოხსნელისთვის\n" +" --list-laplacians\t\tხელმისაწვდომი ლაპლასის ინვერსიის ამომხსნელების სია\n" +" --help-laplacian \tდახმარების გამოტანა მოცემული ლაპლასის " +"ინვერსიის ამომხსნელისთვის\n" +" --list-laplacexz\t\tხელმისაწვდომი LaplaceXZ ინვერსიის ამომხსნელების სია\n" +" --help-laplacexz \tდახმარების გამოტანა მოცემული LaplaceXZ " +"ინვერსიის ამომხსნელისთვის\n" +" --list-invertpars\t\tხელმისაწვდომი InvertPar ამომხსნელების სია\n" +" --help-invertpar \tდახმარების გამოტანა მოცემული InvertPar " +"ამომხსნელისთვის\n" +" --list-rkschemes\t\tხელმისაწვდომი Runge-Kutta სქემების სიის გამოტანა\n" +" --help-rkscheme \tდახმარების გამოტანა მოცემული Runge-Kutta " +"სქემისთვის\n" +" --list-meshes\t\t\tხელმისაწვდომი ბადეების სია\n" +" --help-mesh \t\tდახმარების გამოტანა მოცემული ბადისთვის\n" +" --list-xzinterpolations\tხელმისაწვდომი XZInterpolation-ების სიის გამოტანა\n" +" --help-xzinterpolation \tდახმარების გამოტანა მოცემული " +"XZInterpolation-ისთვის\n" +" --list-zinterpolations\tხელმისაწვდომი ZInterpolation-ების სიის გამოტანა\n" +" --help-zinterpolation \tდახმარების გამოტანა მოცემული " +"ZInterpolation-ისთვის\n" +" -h, --help\t\t\tეს შეტყობინება\n" +" restart [append]\t\tსიმულაციის თავიდან გაშვება. თუ მითითებულია append, " +"გამოტანილი ინფორმაცია არსებულ გამოტანილ ფაილებს ბოლოში მიეწერება. წინააღმდეგ " +"შემთხვევაში კი მოხდება თავზე გადაწერა\n" +" VAR=VALUE\t\t\tმიუთითეთ VALUE შეყვანის პარამეტრისტვის VAR\n" +"\n" +"ყველა შესაძლო შეყვანის პარამეტრის სანახავად იხილეთ სახელმძღვანელო, ან/და " +"ფიზიკის მოდელის წყარო (მაგ: {:s}.cxx)\n" + +#: ../src/bout++.cxx:386 +msgid " -c, --color\t\t\tColor output using bout-log-color\n" +msgstr " -c, --color\t\t\tფერადი გამოტანა bout-log-color-ის გამოყენეებით\n" + +#: ../include/bout/options.hxx:899 +msgid ") overwritten with:" +msgstr ") თავსე გადაწერილია რით:" + +#: ../src/bout++.cxx:557 +#, c++-format +msgid ", level {}" +msgstr ", დონე {}" + +#: ../tests/unit/src/test_bout++.cxx:352 +msgid "4 of 8" +msgstr "4 8-დან" + +#: ../src/sys/options.cxx:895 +msgid "All options used\n" +msgstr "ყველა გამოყენებული პარამეტრი\n" + +#: ../src/bout++.cxx:535 +#, c++-format +msgid "BOUT++ version {:s}\n" +msgstr "BOUT++ ვერსია {:s}\n" + +#: ../src/bout++.cxx:147 +msgid "Bad command line arguments:\n" +msgstr "არასწორი ბრძანების სტრიქონის არგუმენტები:\n" + +#: ../src/sys/expressionparser.cxx:192 +#, c++-format +msgid "Boolean operator argument {:e} is not a bool" +msgstr "ლოგიკური ოპერატორის არგუმენტი {:e} ლოგიკური არაა" + +#: ../src/mesh/impls/bout/boutmesh.cxx:595 +msgid "Boundary regions in this processor: " +msgstr "სასაზღვრო რეგიონები ამ პროცესორში: " + +#: ../src/mesh/impls/bout/boutmesh.cxx:348 +#, c++-format +msgid "Cannot split {:d} X points equally between {:d} processors\n" +msgstr "შეუძლებელია {:d} X წერტილის თანაბრად გაყოფა {:d} პროცესორს შორის\n" + +#: ../src/bout++.cxx:829 +msgid "Check if a file exists, and exit if it does." +msgstr "შემოწმება, არსებობს, თუ არა ფაილი და გასვლა, თუ კი." + +#: ../src/bout++.cxx:540 +#, c++-format +msgid "" +"Code compiled on {:s} at {:s}\n" +"\n" +msgstr "" +"კოდის კომპილაცია მოხდა მანქანაზე {:s} დრო: {:s}\n" +"\n" + +#: ../src/sys/optionsreader.cxx:130 +msgid "Command line" +msgstr "ბრძანების სტრიქონი" + +#: ../src/bout++.cxx:551 ../tests/unit/src/test_bout++.cxx:358 +msgid "Compile-time options:\n" +msgstr "კომპილაციის დროის პარამეტრები:\n" + +#: ../tests/unit/src/test_bout++.cxx:362 +msgid "Compiled with flags" +msgstr "კომპილაციის ალმები" + +#: ../src/mesh/impls/bout/boutmesh.cxx:604 +msgid "Constructing default regions" +msgstr "ნაგულისხმევი რეგიონების აგება" + +#: ../src/bout++.cxx:527 +#, c++-format +msgid "Could not create PID file {:s}" +msgstr "ვერ შევქმენი PID ფაილი {:s}" + +#: ../src/mesh/impls/bout/boutmesh.cxx:309 +msgid "" +"Could not find a valid value for NXPE. Try a different number of processors." +msgstr "" +"NXPE-ისთვის სწორი მნიშვნელობის პოვნა შეუძლებელია. სცადეთ პროცესორების სხვა " +"რაოდენობა." + +#: ../src/sys/options/options_ini.cxx:160 +#, c++-format +msgid "Could not open output file '{:s}'\n" +msgstr "გახსნის შეცდომა გამოტანის ფაილისთვის '{:s}'\n" + +#: ../src/bout++.cxx:657 +#, c++-format +msgid "Could not open {:s}/{:s}.{:d} for writing" +msgstr "{:s}/{:s}.{:d}-ის ჩასაწერად გახსნა შეუძლებელია" + +#. Error reading +#: ../src/mesh/mesh.cxx:543 +#, c++-format +msgid "Could not read integer array '{:s}'\n" +msgstr "წაკითხვის შეცდომა მთელი რიცხვების მასივისთვის '{:s}'\n" + +#. Failed . Probably not important enough to stop the simulation +#: ../src/bout++.cxx:637 +msgid "Could not run bout-log-color. Make sure it is in your PATH\n" +msgstr "bout-log-color-ის გაშვება შეუძლებელია. დარწმუნდით, რომ ის თქვენს PATH-შია\n" + +#: ../src/solver/solver.cxx:772 +#, c++-format +msgid "Couldn't add Monitor: {:g} is not a multiple of {:g}!" +msgstr "მონიტორის დამატება შეუძლებელია: {:g} {:g}-ის ნამრავლი არაა!" + +#: ../src/sys/expressionparser.cxx:312 +#, c++-format +msgid "" +"Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so " +"you\n" +"may need to change your input file.\n" +"{}" +msgstr "" +"გენერატორი '{}' აღმოჩენილი არაა. BOUT++-ის გამოსახულებები ახლა დიდ და პატარა " +"ასოებს ანსხვავებს, ასე რომ,\n" +"შეიძლება, თქვენი შეყვანის ფაილის შემოწმება დაგჭირდეთ.\n" +"{}" + +#: ../src/mesh/mesh.cxx:587 +#, c++-format +msgid "Couldn't find region {:s} in regionMap2D" +msgstr "regionMap2D-ში რეგიონი {:s} აღმოჩენილი არაა" + +#: ../src/mesh/mesh.cxx:571 ../src/mesh/mesh.cxx:579 +#, c++-format +msgid "Couldn't find region {:s} in regionMap3D" +msgstr "regionMap3D-ში რეგიონი {:s} აღმოჩენილი არაა" + +#: ../src/mesh/mesh.cxx:595 +#, c++-format +msgid "Couldn't find region {:s} in regionMapPerp" +msgstr "regionMapPerp-ში რეგიონი {:s} აღმოჩენილი არაა" + +#. Convert any exceptions to something a bit more useful +#: ../src/sys/options.cxx:361 +#, c++-format +msgid "Couldn't get {} from option {:s} = '{:s}': {}" +msgstr "{}-ის მიღება შეუძლებელია პარამეტრიდან {:s} = '{:s}': {}" + +#: ../src/bout++.cxx:515 +#, c++-format +msgid "DataDir \"{:s}\" does not exist or is not accessible\n" +msgstr "მონაცემთა საქაღალდე \"{:s}\" არ არსებობს, ან წვდომადი არაა\n" + +#: ../src/bout++.cxx:512 +#, c++-format +msgid "DataDir \"{:s}\" is not a directory\n" +msgstr "მონაცემთა საქაღალდე \"{:s}\" საქაღალდე არაა\n" + +#: ../src/solver/solver.cxx:671 +msgid "ERROR: Solver is already initialised\n" +msgstr "შეცდომა: ამომხსნელი უკვე ინიციალიზებულია\n" + +#: ../src/bout++.cxx:216 +#, c++-format +msgid "Error encountered during initialisation: {:s}\n" +msgstr "ინიციალიზაციისას აღმოჩენილია შეცდომა: {:s}\n" + +#: ../src/bout++.cxx:751 +msgid "Error whilst writing settings" +msgstr "შეცდომა პარამეტრების ჩაწერისას" + +#: ../src/mesh/impls/bout/boutmesh.cxx:323 +#, c++-format +msgid "Error: nx must be greater than 2 times MXG (2 * {:d})" +msgstr "" +"შეცდომა: nx უფრო მეტი უნდა იყოს, ვიდრე ორზე გამრავლებული MXG (2 * {:d})" + +#: ../src/solver/solver.cxx:520 +msgid "Failed to initialise solver-> Aborting\n" +msgstr "ამომხსნელის ინიციალიზაცია ჩავარდა -> შესრულება შეწყდა\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:281 +#, c++-format +msgid "Finding value for NXPE (ideal = {:f})\n" +msgstr "ვეძებ მნიშვნელობას NXPE-ისთვის (იდეალურია = {:f})\n" + +#: ../src/solver/solver.cxx:674 +msgid "Initialising solver\n" +msgstr "ამომხსნელის ინიციალიზაცია\n" + +#: ../src/bout++.cxx:501 +msgid "" +"Input and output file for settings must be different.\n" +"Provide -o to avoid this issue.\n" +msgstr "" +"შეყვანის და გამოტანის ფაილი პარამეტრებისთვის უნდა განსხვავდებოდეს.\n" +"ამ პრობლემის თავიდან ასაცილებლად მიუთითეთ -o .\n" + +#: ../src/sys/optionsreader.cxx:76 +msgid "Invalid command line option '-' found - maybe check whitespace?" +msgstr "" +"აღმოჩენილია არასწორი ბრძანების სტრიქონის პარამეტრი '-' - შეამოწმეთ ზედმეტი " +"ჰარეები?" + +#: ../src/mesh/impls/bout/boutmesh.cxx:436 +msgid "Loading mesh" +msgstr "ბადის ჩატვირთვა" + +#: ../src/mesh/impls/bout/boutmesh.cxx:451 +msgid "Mesh must contain nx" +msgstr "ბადე უნდა შეიცავდეს nx-ს" + +#: ../src/mesh/impls/bout/boutmesh.cxx:455 +msgid "Mesh must contain ny" +msgstr "ბადე უნდა შეიცავდეს ny-ს" + +#. Not found +#: ../src/mesh/mesh.cxx:547 +#, c++-format +msgid "Missing integer array {:s}\n" +msgstr "აკლია მთელი რიცხვების მასივი {:s}\n" + +#: ../src/solver/solver.cxx:911 +#, c++-format +msgid "Monitor signalled to quit (exception {})\n" +msgstr "მონიტორმა გასვლის სიგნალი გამოაგზავნა (გამონაკლისი {})\n" + +#: ../src/solver/solver.cxx:889 +#, c++-format +msgid "Monitor signalled to quit (return code {})" +msgstr "მონიტორმა გასვლის სიგნალი გამოაგზავნა (დაბრუნების კოდი {})" + +#: ../src/bout++.cxx:834 +msgid "Name of file whose existence triggers a stop" +msgstr "ფაილის სახელი, რომლის არსებობაც გაჩერებას იწვევს" + +#: ../src/mesh/impls/bout/boutmesh.cxx:601 +msgid "No boundary regions in this processor" +msgstr "ამ პროცესორში სასაზღვრო რეგიონები არ არის" + +#: ../src/mesh/impls/bout/boutmesh.cxx:586 +msgid "No boundary regions; domain is periodic\n" +msgstr "სასაზღვრო რეგიონების გარეშე. დომენი პერიოდულია\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:245 +#, c++-format +msgid "" +"Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n" +msgstr "" +"პროცესორების რაოდენობა ({:d}), რომლებიც არ იყოფა NP-ებზე x მიმართულებით " +"({:d})\n" + +#: ../src/mesh/impls/bout/boutmesh.cxx:258 +#, c++-format +msgid "" +"Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n" +msgstr "" +"პროცესორების რაოდენობა ({:d}), რომლებიც არ იყოფა NP-ებზე y მიმართულებით " +"({:d})\n" + +#. Less than 2 time-steps left +#: ../src/bout++.cxx:908 +#, c++-format +msgid "Only {:e} seconds ({:.2f} steps) left. Quitting\n" +msgstr "დარჩა მხოლოდ {:e} წამი ({:.2f} ნაბიჯი). გასვლა\n" + +#: ../src/sys/options.cxx:382 ../src/sys/options.cxx:398 +#: ../src/sys/options.cxx:441 ../src/sys/options.cxx:471 +#: ../src/sys/options.cxx:745 ../src/sys/options.cxx:767 +#: ../src/sys/options.cxx:789 +#, c++-format +msgid "Option {:s} has no value" +msgstr "პარამეტრს {:s} მნიშვნელობა არ აქვს" + +#. Doesn't exist +#: ../src/sys/options.cxx:172 +#, c++-format +msgid "Option {:s}:{:s} does not exist" +msgstr "პარამეტრი {:s}:{:s} არ არსებობს" + +#: ../include/bout/options.hxx:904 +#, c++-format +msgid "" +"Options: Setting a value from same source ({:s}) to new value '{:s}' - old " +"value was '{:s}'." +msgstr "" +"პარამეტრები: მნიშვნელობის დაყენება იგივე წყაროდან ({:s}) ახალ მნიშვნელობაზე " +"'{:s}' - ძველი მნიშვნელობა იყო '{:s}'." + +#: ../src/mesh/impls/bout/boutmesh.cxx:588 +msgid "Possible boundary regions are: " +msgstr "შესაძლო სასაზღვრო რეგიონებია: " + +#: ../src/bout++.cxx:545 +#, c++-format +msgid "" +"Processor number: {:d} of {:d}\n" +"\n" +msgstr "" +"პროცესორის ნომერი: {:d} {:d}-დან\n" +"\n" + +#: ../src/mesh/mesh.cxx:642 +#, c++-format +msgid "Registered region 2D {:s}" +msgstr "რეგისტრირებული რეგიონი 2D {:s}" + +#: ../src/mesh/mesh.cxx:632 +#, c++-format +msgid "Registered region 3D {:s}" +msgstr "რეგისტრირებული რეგიონი 3D {:s}" + +#: ../src/mesh/mesh.cxx:652 +#, c++-format +msgid "Registered region Perp {:s}" +msgstr "რეგისტრირებულია რეგიონი Perp {:s}" + +#: ../src/bout++.cxx:536 +#, c++-format +msgid "Revision: {:s}\n" +msgstr "რევიზია: {:s}\n" + +#: ../src/solver/solver.cxx:587 +msgid "Run time : " +msgstr "გაშვებულობის დრო : " + +#. / Run the solver +#: ../src/solver/solver.cxx:533 +msgid "" +"Running simulation\n" +"\n" +msgstr "" +"სიმულაციის გაშვება\n" +"\n" + +#: ../tests/unit/src/test_bout++.cxx:359 +msgid "Signal" +msgstr "სიგნალი" + +#: ../src/bout++.cxx:876 +msgid "" +"Sim Time | RHS evals | Wall Time | Calc Inv Comm I/O SOLVER\n" +"\n" +msgstr "" +"სიმ. დრო | RHS გამოთვ. | კედლ. დრო | გამოთვ. ინვ. კავშ. I/O ამომხს.\n" +"\n" + +#: ../src/bout++.cxx:879 +msgid "" +"Sim Time | RHS_e evals | RHS_I evals | Wall Time | Calc Inv " +"Comm I/O SOLVER\n" +"\n" +msgstr "" +"სიმ. დრო | RHS_e გამოთვ. | RHS_I გამოთვ. | კედლ. დრო | გამოთვ. ინვ. კავშ. " +"I/O ამომხს.\n" +"\n" + +#: ../src/solver/solver.cxx:514 +#, c++-format +msgid "Solver running for {:d} outputs with monitor timestep of {:e}\n" +msgstr "" +"ამომხსნელი გაშვებულია {:d} გამოტანისთვის მონიტორინგის დროის ნაბიჯით {:e}\n" + +#: ../src/solver/solver.cxx:510 +#, c++-format +msgid "Solver running for {:d} outputs with output timestep of {:e}\n" +msgstr "" +"ამომხსნელი გაშვებულია {:d} გამოტანისთვის გამოტანის დროის ნაბიჯით {:e}\n" + +#: ../src/solver/solver.cxx:788 +#, c++-format +msgid "" +"Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) after init is " +"called!" +msgstr "" +"Solver::addMonitor: init-ის გამოძახების შემდეგ დროის ნაბიჯს ({:g}-და {:g}-" +"ზე) ვერ შეამცირებთ!" + +#: ../src/solver/solver.cxx:1289 +#, c++-format +msgid "" +"Time derivative at wrong location - Field is at {:s}, derivative is at {:s} " +"for field '{:s}'\n" +msgstr "" +"დროითი წარმოებული არასწორ მდებარეობაზეა - ველი არის {:s}-ზე, წარმოებული კი " +"{:s}-ზე ველისთვის '{:s}'\n" + +#: ../src/solver/solver.cxx:1494 +#, c++-format +msgid "Time derivative for variable '{:s}' not set" +msgstr "ცვლადისთვის '{:s}' დროის წარმოებული დაყენებული არაა" + +#: ../src/mesh/mesh.cxx:638 +#, c++-format +msgid "Trying to add an already existing region {:s} to regionMap2D" +msgstr "მცდელობა, დაემატოს უკვე არსებული რეგიონი {:s} regionMap2D-ს" + +#: ../src/mesh/mesh.cxx:614 +#, c++-format +msgid "Trying to add an already existing region {:s} to regionMap3D" +msgstr "მცდელობა, დაემატოს უკვე არსებული რეგიონი {:s} regionMap3D-ს" + +#: ../src/mesh/mesh.cxx:649 +#, c++-format +msgid "Trying to add an already existing region {:s} to regionMapPerp" +msgstr "მცდელობა, დაემატოს უკვე არსებული რეგიონი {:s} regionMapPerp-ს" + +#: ../src/sys/options.cxx:112 ../src/sys/options.cxx:151 +#, c++-format +msgid "" +"Trying to index Option '{0}' with '{1}', but '{0}' is a value, not a " +"section.\n" +"This is likely the result of clashing input options, and you may have to " +"rename one of them.\n" +msgstr "" +"მიმდინარეობს პარამეტრის '{0}' ინდექსაცია '{1}'-ით, მაგრამ '{0}' მნიშვნელობაა " +"და არა სექცია.\n" +"ეს, სავარაუდოდ, კონფლიქტური შეყვანის პარამეტრების შედეგია და შეიძლება ერთ-" +"ერთის სახელის გადარქმევა დაგჭირდეთ.\n" + +#: ../src/mesh/coordinates.cxx:1464 +msgid "" +"Unrecognised paralleltransform option.\n" +"Valid choices are 'identity', 'shifted', 'fci'" +msgstr "" +"უცნობი პარამეტრია paralleltransform.\n" +"დასაშვები მნიშვნელობებია 'identity', 'shifted', 'fci'" + +#: ../src/sys/options.cxx:899 +msgid "Unused options:\n" +msgstr "გამოუყენებელი პარამეტრები:\n" + +#: ../src/bout++.cxx:446 +#, c++-format +msgid "Usage is {:s} -d \n" +msgstr "გამოყენება: {:s} -d <მონაცემების_სააღალდე>\n" + +#: ../src/bout++.cxx:455 +#, c++-format +msgid "Usage is {:s} -f \n" +msgstr "გამოყენება: {:s} -f <პარამეტრების_ფაილის_სახელი>\n" + +#: ../src/bout++.cxx:473 +#, c++-format +msgid "Usage is {:s} -l \n" +msgstr "გამოყენება: {:s} -l <ჟურნალის_ფაილის_სახელი>\n" + +#: ../src/bout++.cxx:464 +#, c++-format +msgid "Usage is {:s} -o \n" +msgstr "გამოყენება: {:s} -o <პარამეტრების_ფაილის_სახელი>\n" + +#: ../src/bout++.cxx:360 +#, c++-format +msgid "Usage is {} {} \n" +msgstr "გამოყენება: {} {} <სახელი>\n" + +#: ../tests/unit/src/test_bout++.cxx:32 ../tests/unit/src/test_bout++.cxx:46 +msgid "Usage:" +msgstr "გამოყენება:" + +#. Print help message -- note this will be displayed once per processor as we've not +#. started MPI yet. +#: ../src/bout++.cxx:374 +#, c++-format +msgid "" +"Usage: {:s} [-d ] [-f ] [restart [append]] " +"[VAR=VALUE]\n" +msgstr "" +"გამოყენება: {:s} [-d <მონაცემთა_საქაღალდე>] [-f " +"<პარამეტრების_ფაილის_სახელი>] [restart [append]] [VAR=VALUE]\n" + +#. restart file should be written by physics model +#: ../src/solver/solver.cxx:927 +msgid "User signalled to quit. Returning\n" +msgstr "მომხმარებელმა მიგვანიშნა, მუშაობა დაგვემთავრებინა\n" + +#: ../src/sys/options.cxx:486 +#, c++-format +msgid "Value for option {:s} = {:e} is not a bool" +msgstr "მნიშვნელობა პარამეტრისთვის {:s} = {:e} ლოგიკური არაა" + +#: ../src/sys/options.cxx:426 +#, c++-format +msgid "Value for option {:s} = {:e} is not an integer" +msgstr "მნიშვნელობა პარამეტრისთვის {:s} = {:e} მთელი რიცხვი არაა" + +#: ../src/sys/options.cxx:456 +#, c++-format +msgid "Value for option {:s} cannot be converted to a BoutReal" +msgstr "მნიშვნელობა პარამეტრისთვის {:s} BoutReal-ში გადაყვანილი ვერ იქნება" + +#: ../src/sys/options.cxx:623 +#, c++-format +msgid "Value for option {:s} cannot be converted to a Field2D" +msgstr "მნიშვნელობა პარამეტრისთვის {:s} Field2D-ში გადაყვანილი ვერ იქნება" + +#: ../src/sys/options.cxx:571 +#, c++-format +msgid "Value for option {:s} cannot be converted to a Field3D" +msgstr "მნიშვნელობა პარამეტრისთვის {:s} Field3D-ში გადაყვანილი ვერ იქნება" + +#: ../src/sys/options.cxx:705 +#, c++-format +msgid "Value for option {:s} cannot be converted to a FieldPerp" +msgstr "მნიშვნელობა პარამეტრისთვის {:s} FieldPerp-ში გადაყვანილი ვერ იქნება" + +#: ../src/sys/options.cxx:491 +#, c++-format +msgid "Value for option {:s} cannot be converted to a bool" +msgstr "მნიშვნელობა პარამეტრისთვის {:s} bool-ში გადაყვანილი ვერ იქნება" + +#: ../src/sys/options.cxx:751 +#, c++-format +msgid "Value for option {:s} cannot be converted to an Array" +msgstr "" +"მნიშვნელობა პარამეტრისთვის {:s} Array-ში გადაყვანილი ვერ იქნება" + +#: ../src/sys/options.cxx:773 +#, c++-format +msgid "Value for option {:s} cannot be converted to an Matrix" +msgstr "" +"მნიშვნელობა პარამეტრისთვის {:s} Matrix-ში გადაყვანილი ვერ იქნება" + +#: ../src/sys/options.cxx:795 +#, c++-format +msgid "Value for option {:s} cannot be converted to an Tensor" +msgstr "" +"მნიშვნელობა პარამეტრისთვის {:s} Tensor-ში გადაყვანილი ვერ იქნება" + +#. Another type which can't be converted +#: ../src/sys/options.cxx:418 +#, c++-format +msgid "Value for option {:s} is not an integer" +msgstr "მნიშვნელობა პარამეტრისთვის {:s} მთელი რიცხვი არაა" + +#: ../src/solver/solver.cxx:1240 ../src/solver/solver.cxx:1246 +#, c++-format +msgid "Variable '{:s}' not initialised" +msgstr "ცვლადი '{:s}' ინიციალიზებული არაა" + +#: ../src/mesh/impls/bout/boutmesh.cxx:467 +#, c++-format +msgid "" +"WARNING: Number of toroidal points should be 2^n for efficient FFT " +"performance -- consider changing MZ ({:d}) if using FFTs\n" +msgstr "" +"გაფრთხილება: FFT-ის ეფექტური წარმადობისთვის ტოროიდული წერტილების რაოდენობა " +"უნდა იყოს 2^n -- თუ FFT-ებს იყენებთ, განიხილეთ MZ-ის ({:d}) შეცვლა\n" + +#: ../src/mesh/coordinates.cxx:635 +msgid "WARNING: extrapolating input mesh quantities into x-boundary cells\n" +msgstr "გაფრთხილება: შეყვანილი ბადის სიდიდეები ექსტრაპოლირდება x-სასაზღვრო უჯრედებში\n" + +#: ../src/mesh/coordinates.cxx:412 +msgid "" +"WARNING: extrapolating input mesh quantities into x-boundary cells. Set " +"option extrapolate_x=false to disable this.\n" +msgstr "" +"გაფრთხილება: შეყვანილი ბადის სიდიდეები ექსტრაპოლირდება x-სასაზღვრო უჯრედებში. " +"ამის გასათიშად დააყენეთ პარამეტრი extrapolate_x=false.\n" + +#: ../src/mesh/coordinates.cxx:640 +msgid "WARNING: extrapolating input mesh quantities into y-boundary cells\n" +msgstr "გაფრთხილება: შეყვანილი ბადის სიდიდეები ექსტრაპოლირდება y-სასაზღვრო უჯრედებში\n" + +#: ../src/mesh/coordinates.cxx:417 +msgid "" +"WARNING: extrapolating input mesh quantities into y-boundary cells. Set " +"option extrapolate_y=false to disable this.\n" +msgstr "" +"გაფრთხილება: შეყვანილი ბადის სიდიდეები ექსტრაპოლირდება y-სასაზღვრო უჯრედებში. " +"ამის გასათიშად დააყენეთ პარამეტრი extrapolate_y=false.\n" + +#: ../src/bout++.cxx:825 +msgid "Wall time limit in hours. By default (< 0), no limit" +msgstr "კედლის დროის ზღვარი საათებში. ნაგულისხმევად (< 0) ზღვარი არ არსებობს" + +#: ../src/sys/optionsreader.cxx:42 +#, c++-format +msgid "Writing options to file {:s}\n" +msgstr "პარამეტრების ჩაწერა ფაილში {:s}\n" + +#. / The source label given to default values +#: ../src/sys/options.cxx:34 +msgid "default" +msgstr "ნაგულისხმევი" diff --git a/locale/libbout.pot b/locale/libbout.pot index 3895001ba0..2ef0618dd0 100644 --- a/locale/libbout.pot +++ b/locale/libbout.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-12 09:17+0100\n" +"POT-Creation-Date: 2025-08-13 23:37+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,65 +17,70 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:191 +#: ../src/mesh/impls/bout/boutmesh.cxx:182 #, c++-format msgid "" "\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:224 +#: ../src/mesh/impls/bout/boutmesh.cxx:215 #, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:199 +#: ../src/mesh/impls/bout/boutmesh.cxx:190 #, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:309 +#: ../src/mesh/impls/bout/boutmesh.cxx:300 msgid "\t -> Good value\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:180 +#: ../src/mesh/impls/bout/boutmesh.cxx:171 #, c++-format msgid "" "\t -> Leg region jyseps1_1+1 ({:d}) must be a multiple of MYSUB ({:d})\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:215 +#: ../src/mesh/impls/bout/boutmesh.cxx:206 #, c++-format msgid "" "\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:232 +#: ../src/mesh/impls/bout/boutmesh.cxx:223 #, c++-format msgid "" "\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a multiple of " "MYSUB ({:d})\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:208 +#: ../src/mesh/impls/bout/boutmesh.cxx:199 #, c++-format msgid "" "\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:175 +#: ../src/mesh/impls/bout/boutmesh.cxx:166 #, c++-format msgid "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n" msgstr "" #: ../src/bout++.cxx:575 #, c++-format +msgid "\tADIOS2 support {}\n" +msgstr "" + +#: ../src/bout++.cxx:583 +#, c++-format msgid "\tBacktrace in exceptions {}\n" msgstr "" @@ -83,40 +88,40 @@ msgstr "" #. Processors divide equally #. Mesh in X divides equally #. Mesh in Y divides equally -#: ../src/mesh/impls/bout/boutmesh.cxx:297 +#: ../src/mesh/impls/bout/boutmesh.cxx:288 #, c++-format msgid "\tCandidate value: {:d}\n" msgstr "" -#: ../src/bout++.cxx:576 +#: ../src/bout++.cxx:584 #, c++-format msgid "\tColour in logs {}\n" msgstr "" -#: ../src/bout++.cxx:594 +#: ../src/bout++.cxx:599 msgid "\tCommand line options for this run : " msgstr "" #. The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings #. which could cause problems (e.g. terminate strings). -#: ../src/bout++.cxx:590 +#: ../src/bout++.cxx:595 #, c++-format msgid "\tCompiled with flags : {:s}\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:324 +#: ../src/mesh/impls/bout/boutmesh.cxx:315 #, c++-format msgid "" "\tDomain split (NXPE={:d}, NYPE={:d}) into domains (localNx={:d}, " "localNy={:d})\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:364 +#: ../src/mesh/impls/bout/boutmesh.cxx:357 #, c++-format msgid "\tERROR: Cannot split {:d} Y points equally between {:d} processors\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:372 +#: ../src/mesh/impls/bout/boutmesh.cxx:365 #, c++-format msgid "\tERROR: Cannot split {:d} Z points equally between {:d} processors\n" msgstr "" @@ -133,31 +138,31 @@ msgstr "" msgid "\tEmpty key or value in command line '{:s}'\n" msgstr "" -#: ../src/bout++.cxx:582 +#: ../src/bout++.cxx:587 #, c++-format msgid "\tExtra debug output {}\n" msgstr "" -#: ../src/bout++.cxx:561 +#: ../src/bout++.cxx:568 #, c++-format msgid "\tFFT support {}\n" msgstr "" -#: ../src/bout++.cxx:585 +#: ../src/bout++.cxx:590 #, c++-format msgid "\tField name tracking {}\n" msgstr "" -#: ../src/bout++.cxx:583 +#: ../src/bout++.cxx:588 #, c++-format msgid "\tFloating-point exceptions {}\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:440 +#: ../src/mesh/impls/bout/boutmesh.cxx:476 msgid "\tGrid size: " msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:463 +#: ../src/mesh/impls/bout/boutmesh.cxx:499 msgid "\tGuard cells (x,y,z): " msgstr "" @@ -168,17 +173,17 @@ msgid "" "\tLine: {:s}" msgstr "" -#: ../src/bout++.cxx:563 +#: ../src/bout++.cxx:570 #, c++-format msgid "\tLAPACK support {}\n" msgstr "" -#: ../src/bout++.cxx:586 +#: ../src/bout++.cxx:591 #, c++-format msgid "\tMessage stack {}\n" msgstr "" -#: ../src/bout++.cxx:560 +#: ../src/bout++.cxx:567 #, c++-format msgid "\tMetrics mode is {}\n" msgstr "" @@ -188,35 +193,31 @@ msgstr "" msgid "\tMultiple '=' in command-line argument '{:s}'\n" msgstr "" -#: ../src/bout++.cxx:562 +#: ../src/bout++.cxx:569 #, c++-format msgid "\tNatural language support {}\n" msgstr "" -#: ../src/bout++.cxx:567 +#: ../src/bout++.cxx:574 #, c++-format msgid "\tNetCDF support {}{}\n" msgstr "" -#: ../src/bout++.cxx:577 +#: ../src/bout++.cxx:585 #, c++-format -msgid "\tOpenMP parallelisation {}" +msgid "\tOpenMP parallelisation {}, using {} threads\n" msgstr "" #. Mark the option as used #. Option not found -#: ../src/sys/options.cxx:311 ../src/sys/options.cxx:380 -#: ../src/sys/options.cxx:415 ../src/sys/options.cxx:457 -#: ../src/sys/options.cxx:717 ../src/sys/options.cxx:744 -#: ../src/sys/options.cxx:771 ../include/bout/options.hxx:516 -#: ../include/bout/options.hxx:549 ../include/bout/options.hxx:573 -#: ../include/bout/options.hxx:820 +#: ../include/bout/options.hxx:586 ../include/bout/options.hxx:619 +#: ../include/bout/options.hxx:643 ../include/bout/options.hxx:896 msgid "\tOption " msgstr "" -#: ../src/sys/options.cxx:447 +#: ../src/sys/options.cxx:369 #, c++-format -msgid "\tOption '{:s}': Boolean expected. Got '{:s}'\n" +msgid "\tOption {} = {}" msgstr "" #: ../src/sys/options/options_ini.cxx:70 @@ -224,62 +225,62 @@ msgstr "" msgid "\tOptions file '{:s}' not found\n" msgstr "" -#: ../src/bout++.cxx:568 +#: ../src/bout++.cxx:576 #, c++-format msgid "\tPETSc support {}\n" msgstr "" -#: ../src/bout++.cxx:571 +#: ../src/bout++.cxx:579 #, c++-format msgid "\tPVODE support {}\n" msgstr "" -#: ../src/bout++.cxx:557 +#: ../src/bout++.cxx:564 msgid "\tParallel NetCDF support disabled\n" msgstr "" -#: ../src/bout++.cxx:555 +#: ../src/bout++.cxx:562 msgid "\tParallel NetCDF support enabled\n" msgstr "" -#: ../src/bout++.cxx:569 +#: ../src/bout++.cxx:577 #, c++-format msgid "\tPretty function name support {}\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:437 +#: ../src/mesh/impls/bout/boutmesh.cxx:473 msgid "\tRead nz from input grid file\n" msgstr "" -#: ../src/mesh/mesh.cxx:238 +#: ../src/mesh/mesh.cxx:249 msgid "\tReading contravariant vector " msgstr "" -#: ../src/mesh/mesh.cxx:231 ../src/mesh/mesh.cxx:252 +#: ../src/mesh/mesh.cxx:242 ../src/mesh/mesh.cxx:263 msgid "\tReading covariant vector " msgstr "" -#: ../src/bout++.cxx:548 +#: ../src/bout++.cxx:555 #, c++-format msgid "\tRuntime error checking {}" msgstr "" -#: ../src/bout++.cxx:573 +#: ../src/bout++.cxx:581 #, c++-format msgid "\tSLEPc support {}\n" msgstr "" -#: ../src/bout++.cxx:574 +#: ../src/bout++.cxx:582 #, c++-format msgid "\tSUNDIALS support {}\n" msgstr "" -#: ../src/bout++.cxx:572 +#: ../src/bout++.cxx:580 #, c++-format msgid "\tScore-P support {}\n" msgstr "" -#: ../src/bout++.cxx:584 +#: ../src/bout++.cxx:589 #, c++-format msgid "\tSignal handling support {}\n" msgstr "" @@ -289,7 +290,7 @@ msgstr "" msgid "\tUsing a timestep {:e}\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:577 +#: ../src/mesh/impls/bout/boutmesh.cxx:613 msgid "\tdone\n" msgstr "" @@ -299,7 +300,7 @@ msgid "" "\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n" msgstr "" -#: ../src/bout++.cxx:371 +#: ../src/bout++.cxx:378 msgid "" "\n" " -d \t\tLook in for input/output files\n" @@ -310,7 +311,7 @@ msgid "" " -q, --quiet\t\t\tDecrease verbosity\n" msgstr "" -#: ../src/sys/expressionparser.cxx:302 +#: ../src/sys/expressionparser.cxx:341 #, c++-format msgid "" "\n" @@ -318,14 +319,14 @@ msgid "" " Did you mean '{0}'?" msgstr "" -#: ../src/solver/solver.cxx:580 +#: ../src/solver/solver.cxx:586 #, c++-format msgid "" "\n" "Run finished at : {:s}\n" msgstr "" -#: ../src/solver/solver.cxx:532 +#: ../src/solver/solver.cxx:540 #, c++-format msgid "" "\n" @@ -335,7 +336,7 @@ msgstr "" #. Raw string to help with the formatting of the message, and a #. separate variable so clang-format doesn't barf on the #. exception -#: ../src/sys/options.cxx:1102 +#: ../src/sys/options.cxx:1158 msgid "" "\n" "There were unused input options:\n" @@ -363,7 +364,7 @@ msgid "" "{}" msgstr "" -#: ../src/bout++.cxx:382 +#: ../src/bout++.cxx:389 #, c++-format msgid "" " --print-config\t\tPrint the compile-time configuration\n" @@ -396,55 +397,55 @@ msgid "" "model source (e.g. {:s}.cxx)\n" msgstr "" -#: ../src/bout++.cxx:379 +#: ../src/bout++.cxx:386 msgid " -c, --color\t\t\tColor output using bout-log-color\n" msgstr "" -#: ../include/bout/options.hxx:823 +#: ../include/bout/options.hxx:899 msgid ") overwritten with:" msgstr "" -#: ../src/bout++.cxx:550 +#: ../src/bout++.cxx:557 #, c++-format msgid ", level {}" msgstr "" -#: ../src/bout++.cxx:579 -#, c++-format -msgid ", using {} threads" -msgstr "" - #: ../tests/unit/src/test_bout++.cxx:352 msgid "4 of 8" msgstr "" -#: ../src/sys/options.cxx:868 +#: ../src/sys/options.cxx:895 msgid "All options used\n" msgstr "" -#: ../src/bout++.cxx:528 +#: ../src/bout++.cxx:535 #, c++-format msgid "BOUT++ version {:s}\n" msgstr "" -#: ../src/bout++.cxx:143 +#: ../src/bout++.cxx:147 msgid "Bad command line arguments:\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:559 +#: ../src/sys/expressionparser.cxx:192 +#, c++-format +msgid "Boolean operator argument {:e} is not a bool" +msgstr "" + +#: ../src/mesh/impls/bout/boutmesh.cxx:595 msgid "Boundary regions in this processor: " msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:355 +#: ../src/mesh/impls/bout/boutmesh.cxx:348 #, c++-format msgid "Cannot split {:d} X points equally between {:d} processors\n" msgstr "" -#: ../src/bout++.cxx:818 +#: ../src/bout++.cxx:829 msgid "Check if a file exists, and exit if it does." msgstr "" -#: ../src/bout++.cxx:533 +#: ../src/bout++.cxx:540 #, c++-format msgid "" "Code compiled on {:s} at {:s}\n" @@ -455,7 +456,7 @@ msgstr "" msgid "Command line" msgstr "" -#: ../src/bout++.cxx:544 ../tests/unit/src/test_bout++.cxx:358 +#: ../src/bout++.cxx:551 ../tests/unit/src/test_bout++.cxx:358 msgid "Compile-time options:\n" msgstr "" @@ -463,16 +464,16 @@ msgstr "" msgid "Compiled with flags" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:568 +#: ../src/mesh/impls/bout/boutmesh.cxx:604 msgid "Constructing default regions" msgstr "" -#: ../src/bout++.cxx:520 +#: ../src/bout++.cxx:527 #, c++-format msgid "Could not create PID file {:s}" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:318 +#: ../src/mesh/impls/bout/boutmesh.cxx:309 msgid "" "Could not find a valid value for NXPE. Try a different number of processors." msgstr "" @@ -482,28 +483,28 @@ msgstr "" msgid "Could not open output file '{:s}'\n" msgstr "" -#: ../src/bout++.cxx:652 +#: ../src/bout++.cxx:657 #, c++-format msgid "Could not open {:s}/{:s}.{:d} for writing" msgstr "" #. Error reading -#: ../src/mesh/mesh.cxx:532 +#: ../src/mesh/mesh.cxx:543 #, c++-format msgid "Could not read integer array '{:s}'\n" msgstr "" #. Failed . Probably not important enough to stop the simulation -#: ../src/bout++.cxx:632 +#: ../src/bout++.cxx:637 msgid "Could not run bout-log-color. Make sure it is in your PATH\n" msgstr "" -#: ../src/solver/solver.cxx:765 +#: ../src/solver/solver.cxx:772 #, c++-format msgid "Couldn't add Monitor: {:g} is not a multiple of {:g}!" msgstr "" -#: ../src/sys/expressionparser.cxx:273 +#: ../src/sys/expressionparser.cxx:312 #, c++-format msgid "" "Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so " @@ -512,69 +513,69 @@ msgid "" "{}" msgstr "" -#: ../src/mesh/mesh.cxx:568 +#: ../src/mesh/mesh.cxx:587 #, c++-format msgid "Couldn't find region {:s} in regionMap2D" msgstr "" -#: ../src/mesh/mesh.cxx:560 +#: ../src/mesh/mesh.cxx:571 ../src/mesh/mesh.cxx:579 #, c++-format msgid "Couldn't find region {:s} in regionMap3D" msgstr "" -#: ../src/mesh/mesh.cxx:576 +#: ../src/mesh/mesh.cxx:595 #, c++-format msgid "Couldn't find region {:s} in regionMapPerp" msgstr "" #. Convert any exceptions to something a bit more useful -#: ../src/sys/options.cxx:336 +#: ../src/sys/options.cxx:361 #, c++-format msgid "Couldn't get {} from option {:s} = '{:s}': {}" msgstr "" -#: ../src/bout++.cxx:508 +#: ../src/bout++.cxx:515 #, c++-format msgid "DataDir \"{:s}\" does not exist or is not accessible\n" msgstr "" -#: ../src/bout++.cxx:505 +#: ../src/bout++.cxx:512 #, c++-format msgid "DataDir \"{:s}\" is not a directory\n" msgstr "" -#: ../src/solver/solver.cxx:665 +#: ../src/solver/solver.cxx:671 msgid "ERROR: Solver is already initialised\n" msgstr "" -#: ../src/bout++.cxx:209 +#: ../src/bout++.cxx:216 #, c++-format msgid "Error encountered during initialisation: {:s}\n" msgstr "" -#: ../src/bout++.cxx:744 +#: ../src/bout++.cxx:751 msgid "Error whilst writing settings" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:332 +#: ../src/mesh/impls/bout/boutmesh.cxx:323 #, c++-format msgid "Error: nx must be greater than 2 times MXG (2 * {:d})" msgstr "" -#: ../src/solver/solver.cxx:512 +#: ../src/solver/solver.cxx:520 msgid "Failed to initialise solver-> Aborting\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:290 +#: ../src/mesh/impls/bout/boutmesh.cxx:281 #, c++-format msgid "Finding value for NXPE (ideal = {:f})\n" msgstr "" -#: ../src/solver/solver.cxx:668 +#: ../src/solver/solver.cxx:674 msgid "Initialising solver\n" msgstr "" -#: ../src/bout++.cxx:494 +#: ../src/bout++.cxx:501 msgid "" "Input and output file for settings must be different.\n" "Provide -o to avoid this issue.\n" @@ -584,122 +585,122 @@ msgstr "" msgid "Invalid command line option '-' found - maybe check whitespace?" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:400 +#: ../src/mesh/impls/bout/boutmesh.cxx:436 msgid "Loading mesh" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:415 +#: ../src/mesh/impls/bout/boutmesh.cxx:451 msgid "Mesh must contain nx" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:419 +#: ../src/mesh/impls/bout/boutmesh.cxx:455 msgid "Mesh must contain ny" msgstr "" #. Not found -#: ../src/mesh/mesh.cxx:536 +#: ../src/mesh/mesh.cxx:547 #, c++-format msgid "Missing integer array {:s}\n" msgstr "" -#: ../src/solver/solver.cxx:905 +#: ../src/solver/solver.cxx:911 #, c++-format msgid "Monitor signalled to quit (exception {})\n" msgstr "" -#: ../src/solver/solver.cxx:883 +#: ../src/solver/solver.cxx:889 #, c++-format msgid "Monitor signalled to quit (return code {})" msgstr "" -#: ../src/bout++.cxx:823 +#: ../src/bout++.cxx:834 msgid "Name of file whose existence triggers a stop" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:565 +#: ../src/mesh/impls/bout/boutmesh.cxx:601 msgid "No boundary regions in this processor" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:550 +#: ../src/mesh/impls/bout/boutmesh.cxx:586 msgid "No boundary regions; domain is periodic\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:254 +#: ../src/mesh/impls/bout/boutmesh.cxx:245 #, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:267 +#: ../src/mesh/impls/bout/boutmesh.cxx:258 #, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n" msgstr "" #. Less than 2 time-steps left -#: ../src/bout++.cxx:896 +#: ../src/bout++.cxx:908 #, c++-format msgid "Only {:e} seconds ({:.2f} steps) left. Quitting\n" msgstr "" -#: ../src/sys/options.cxx:303 ../src/sys/options.cxx:345 -#: ../src/sys/options.cxx:393 ../src/sys/options.cxx:428 -#: ../src/sys/options.cxx:703 ../src/sys/options.cxx:730 -#: ../src/sys/options.cxx:757 +#: ../src/sys/options.cxx:382 ../src/sys/options.cxx:398 +#: ../src/sys/options.cxx:441 ../src/sys/options.cxx:471 +#: ../src/sys/options.cxx:745 ../src/sys/options.cxx:767 +#: ../src/sys/options.cxx:789 #, c++-format msgid "Option {:s} has no value" msgstr "" #. Doesn't exist -#: ../src/sys/options.cxx:159 +#: ../src/sys/options.cxx:172 #, c++-format msgid "Option {:s}:{:s} does not exist" msgstr "" -#: ../include/bout/options.hxx:828 +#: ../include/bout/options.hxx:904 #, c++-format msgid "" "Options: Setting a value from same source ({:s}) to new value '{:s}' - old " "value was '{:s}'." msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:552 +#: ../src/mesh/impls/bout/boutmesh.cxx:588 msgid "Possible boundary regions are: " msgstr "" -#: ../src/bout++.cxx:538 +#: ../src/bout++.cxx:545 #, c++-format msgid "" "Processor number: {:d} of {:d}\n" "\n" msgstr "" -#: ../src/mesh/mesh.cxx:609 +#: ../src/mesh/mesh.cxx:642 #, c++-format msgid "Registered region 2D {:s}" msgstr "" -#: ../src/mesh/mesh.cxx:599 +#: ../src/mesh/mesh.cxx:632 #, c++-format msgid "Registered region 3D {:s}" msgstr "" -#: ../src/mesh/mesh.cxx:619 +#: ../src/mesh/mesh.cxx:652 #, c++-format msgid "Registered region Perp {:s}" msgstr "" -#: ../src/bout++.cxx:529 +#: ../src/bout++.cxx:536 #, c++-format msgid "Revision: {:s}\n" msgstr "" -#: ../src/solver/solver.cxx:581 +#: ../src/solver/solver.cxx:587 msgid "Run time : " msgstr "" #. / Run the solver -#: ../src/solver/solver.cxx:525 +#: ../src/solver/solver.cxx:533 msgid "" "Running simulation\n" "\n" @@ -709,64 +710,64 @@ msgstr "" msgid "Signal" msgstr "" -#: ../src/bout++.cxx:865 +#: ../src/bout++.cxx:876 msgid "" "Sim Time | RHS evals | Wall Time | Calc Inv Comm I/O SOLVER\n" "\n" msgstr "" -#: ../src/bout++.cxx:868 +#: ../src/bout++.cxx:879 msgid "" "Sim Time | RHS_e evals | RHS_I evals | Wall Time | Calc Inv " "Comm I/O SOLVER\n" "\n" msgstr "" -#: ../src/solver/solver.cxx:506 +#: ../src/solver/solver.cxx:514 #, c++-format msgid "Solver running for {:d} outputs with monitor timestep of {:e}\n" msgstr "" -#: ../src/solver/solver.cxx:502 +#: ../src/solver/solver.cxx:510 #, c++-format msgid "Solver running for {:d} outputs with output timestep of {:e}\n" msgstr "" -#: ../src/solver/solver.cxx:781 +#: ../src/solver/solver.cxx:788 #, c++-format msgid "" "Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) after init is " "called!" msgstr "" -#: ../src/solver/solver.cxx:1281 +#: ../src/solver/solver.cxx:1289 #, c++-format msgid "" "Time derivative at wrong location - Field is at {:s}, derivative is at {:s} " "for field '{:s}'\n" msgstr "" -#: ../src/solver/solver.cxx:1480 +#: ../src/solver/solver.cxx:1494 #, c++-format msgid "Time derivative for variable '{:s}' not set" msgstr "" -#: ../src/mesh/mesh.cxx:605 +#: ../src/mesh/mesh.cxx:638 #, c++-format msgid "Trying to add an already existing region {:s} to regionMap2D" msgstr "" -#: ../src/mesh/mesh.cxx:595 +#: ../src/mesh/mesh.cxx:614 #, c++-format msgid "Trying to add an already existing region {:s} to regionMap3D" msgstr "" -#: ../src/mesh/mesh.cxx:616 +#: ../src/mesh/mesh.cxx:649 #, c++-format msgid "Trying to add an already existing region {:s} to regionMapPerp" msgstr "" -#: ../src/sys/options.cxx:99 ../src/sys/options.cxx:138 +#: ../src/sys/options.cxx:112 ../src/sys/options.cxx:151 #, c++-format msgid "" "Trying to index Option '{0}' with '{1}', but '{0}' is a value, not a " @@ -775,37 +776,37 @@ msgid "" "rename one of them.\n" msgstr "" -#: ../src/mesh/coordinates.cxx:1462 +#: ../src/mesh/coordinates.cxx:1464 msgid "" "Unrecognised paralleltransform option.\n" "Valid choices are 'identity', 'shifted', 'fci'" msgstr "" -#: ../src/sys/options.cxx:872 +#: ../src/sys/options.cxx:899 msgid "Unused options:\n" msgstr "" -#: ../src/bout++.cxx:439 +#: ../src/bout++.cxx:446 #, c++-format msgid "Usage is {:s} -d \n" msgstr "" -#: ../src/bout++.cxx:448 +#: ../src/bout++.cxx:455 #, c++-format msgid "Usage is {:s} -f \n" msgstr "" -#: ../src/bout++.cxx:466 +#: ../src/bout++.cxx:473 #, c++-format msgid "Usage is {:s} -l \n" msgstr "" -#: ../src/bout++.cxx:457 +#: ../src/bout++.cxx:464 #, c++-format msgid "Usage is {:s} -o \n" msgstr "" -#: ../src/bout++.cxx:353 +#: ../src/bout++.cxx:360 #, c++-format msgid "Usage is {} {} \n" msgstr "" @@ -816,7 +817,7 @@ msgstr "" #. Print help message -- note this will be displayed once per processor as we've not #. started MPI yet. -#: ../src/bout++.cxx:367 +#: ../src/bout++.cxx:374 #, c++-format msgid "" "Usage: {:s} [-d ] [-f ] [restart [append]] " @@ -824,94 +825,99 @@ msgid "" msgstr "" #. restart file should be written by physics model -#: ../src/solver/solver.cxx:921 +#: ../src/solver/solver.cxx:927 msgid "User signalled to quit. Returning\n" msgstr "" -#: ../src/sys/options.cxx:373 +#: ../src/sys/options.cxx:486 +#, c++-format +msgid "Value for option {:s} = {:e} is not a bool" +msgstr "" + +#: ../src/sys/options.cxx:426 #, c++-format msgid "Value for option {:s} = {:e} is not an integer" msgstr "" -#: ../src/sys/options.cxx:408 +#: ../src/sys/options.cxx:456 #, c++-format msgid "Value for option {:s} cannot be converted to a BoutReal" msgstr "" -#: ../src/sys/options.cxx:581 +#: ../src/sys/options.cxx:623 #, c++-format msgid "Value for option {:s} cannot be converted to a Field2D" msgstr "" -#: ../src/sys/options.cxx:529 +#: ../src/sys/options.cxx:571 #, c++-format msgid "Value for option {:s} cannot be converted to a Field3D" msgstr "" -#: ../src/sys/options.cxx:663 +#: ../src/sys/options.cxx:705 #, c++-format msgid "Value for option {:s} cannot be converted to a FieldPerp" msgstr "" -#: ../src/sys/options.cxx:451 +#: ../src/sys/options.cxx:491 #, c++-format msgid "Value for option {:s} cannot be converted to a bool" msgstr "" -#: ../src/sys/options.cxx:709 +#: ../src/sys/options.cxx:751 #, c++-format msgid "Value for option {:s} cannot be converted to an Array" msgstr "" -#: ../src/sys/options.cxx:736 +#: ../src/sys/options.cxx:773 #, c++-format msgid "Value for option {:s} cannot be converted to an Matrix" msgstr "" -#: ../src/sys/options.cxx:763 +#: ../src/sys/options.cxx:795 #, c++-format msgid "Value for option {:s} cannot be converted to an Tensor" msgstr "" #. Another type which can't be converted -#: ../src/sys/options.cxx:365 +#: ../src/sys/options.cxx:418 #, c++-format msgid "Value for option {:s} is not an integer" msgstr "" -#: ../src/solver/solver.cxx:1232 ../src/solver/solver.cxx:1238 +#: ../src/solver/solver.cxx:1240 ../src/solver/solver.cxx:1246 #, c++-format msgid "Variable '{:s}' not initialised" msgstr "" -#: ../src/mesh/impls/bout/boutmesh.cxx:431 +#: ../src/mesh/impls/bout/boutmesh.cxx:467 #, c++-format msgid "" "WARNING: Number of toroidal points should be 2^n for efficient FFT " "performance -- consider changing MZ ({:d}) if using FFTs\n" msgstr "" -#: ../src/mesh/coordinates.cxx:633 +#: ../src/mesh/coordinates.cxx:635 msgid "WARNING: extrapolating input mesh quantities into x-boundary cells\n" msgstr "" -#: ../src/mesh/coordinates.cxx:410 +#: ../src/mesh/coordinates.cxx:412 msgid "" "WARNING: extrapolating input mesh quantities into x-boundary cells. Set " "option extrapolate_x=false to disable this.\n" msgstr "" -#: ../src/mesh/coordinates.cxx:638 +#: ../src/mesh/coordinates.cxx:640 msgid "WARNING: extrapolating input mesh quantities into y-boundary cells\n" msgstr "" -#: ../src/mesh/coordinates.cxx:415 +#: ../src/mesh/coordinates.cxx:417 msgid "" "WARNING: extrapolating input mesh quantities into y-boundary cells. Set " "option extrapolate_y=false to disable this.\n" msgstr "" -#: ../src/bout++.cxx:814 +#: ../src/bout++.cxx:825 msgid "Wall time limit in hours. By default (< 0), no limit" msgstr "" @@ -921,6 +927,6 @@ msgid "Writing options to file {:s}\n" msgstr "" #. / The source label given to default values -#: ../src/sys/options.cxx:15 +#: ../src/sys/options.cxx:34 msgid "default" msgstr "" diff --git a/locale/zh_CN/libbout.po b/locale/zh_CN/libbout.po index 2c53b5392c..f735dd1727 100644 --- a/locale/zh_CN/libbout.po +++ b/locale/zh_CN/libbout.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: BOUT++ 4.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-12 09:17+0100\n" +"POT-Creation-Date: 2025-08-13 23:37+0100\n" "PO-Revision-Date: 2018-10-22 22:56+0100\n" "Last-Translator: \n" "Language-Team: Chinese (simplified)\n" @@ -16,109 +16,131 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:191 +#: ../src/mesh/impls/bout/boutmesh.cxx:182 #, c++-format msgid "" "\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" +"\t -> Core 区域 jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) 必须是 MYSUB ({:d}) 的整数倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:224 +#: ../src/mesh/impls/bout/boutmesh.cxx:215 #, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" +"\t -> Core 区域 jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) 必须是 MYSUB ({:d}) 的整数倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:199 +#: ../src/mesh/impls/bout/boutmesh.cxx:190 #, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" +"\t -> Core 区域 jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) 必须是 MYSUB ({:d}) 的整数倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:309 +#: ../src/mesh/impls/bout/boutmesh.cxx:300 msgid "\t -> Good value\n" msgstr "" +"\t -> 有效值\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:180 +#: ../src/mesh/impls/bout/boutmesh.cxx:171 #, c++-format msgid "" "\t -> Leg region jyseps1_1+1 ({:d}) must be a multiple of MYSUB ({:d})\n" msgstr "" +"\t -> Leg 区域 jyseps1_1+1 ({:d}) 必须是 MYSUB ({:d}) 的整数倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:215 +#: ../src/mesh/impls/bout/boutmesh.cxx:206 #, c++-format msgid "" "\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" +"\t -> leg 区域 jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) 必须是 MYSUB ({:d}) 的整数倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:232 +#: ../src/mesh/impls/bout/boutmesh.cxx:223 #, c++-format msgid "" "\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a multiple of " "MYSUB ({:d})\n" msgstr "" +"\t -> leg 区域 ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) 必须是 MYSUB ({:d}) 的整数倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:208 +#: ../src/mesh/impls/bout/boutmesh.cxx:199 #, c++-format msgid "" "\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" +"\t -> leg 区域 ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) 必须是 MYSUB ({:d}) 的整数倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:175 +#: ../src/mesh/impls/bout/boutmesh.cxx:166 #, c++-format msgid "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n" msgstr "" +"\t -> ny/NYPE ({:d}/{:d} = {:d}) 必须 >= MYG ({:d})\n" #: ../src/bout++.cxx:575 #, c++-format +msgid "\tADIOS2 support {}\n" +msgstr "" +"\tADIOS2 支持 {}\n" + +#: ../src/bout++.cxx:583 +#, c++-format msgid "\tBacktrace in exceptions {}\n" msgstr "" +"\t异常中的回溯 {}\n" #. Loop over all possibilities #. Processors divide equally #. Mesh in X divides equally #. Mesh in Y divides equally -#: ../src/mesh/impls/bout/boutmesh.cxx:297 +#: ../src/mesh/impls/bout/boutmesh.cxx:288 #, c++-format msgid "\tCandidate value: {:d}\n" msgstr "" +"\t候选值:{:d}\n" -#: ../src/bout++.cxx:576 +#: ../src/bout++.cxx:584 #, c++-format msgid "\tColour in logs {}\n" msgstr "" +"\t日志着色 {}\n" -#: ../src/bout++.cxx:594 +#: ../src/bout++.cxx:599 msgid "\tCommand line options for this run : " -msgstr "" +msgstr "\t本次运行的命令行选项:" #. The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings #. which could cause problems (e.g. terminate strings). -#: ../src/bout++.cxx:590 +#: ../src/bout++.cxx:595 #, c++-format msgid "\tCompiled with flags : {:s}\n" msgstr "" +"\t编译标志:{:s}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:324 +#: ../src/mesh/impls/bout/boutmesh.cxx:315 #, c++-format msgid "" "\tDomain split (NXPE={:d}, NYPE={:d}) into domains (localNx={:d}, " "localNy={:d})\n" msgstr "" +"\t域已拆分为 (NXPE={:d}, NYPE={:d}),局部域为 (localNx={:d}, localNy={:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:364 +#: ../src/mesh/impls/bout/boutmesh.cxx:357 #, c++-format msgid "\tERROR: Cannot split {:d} Y points equally between {:d} processors\n" msgstr "" +"\t错误:无法将 {:d} 个 Y 点平均分给 {:d} 个处理器\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:372 +#: ../src/mesh/impls/bout/boutmesh.cxx:365 #, c++-format msgid "\tERROR: Cannot split {:d} Z points equally between {:d} processors\n" msgstr "" +"\t错误:无法将 {:d} 个 Z 点平均分给 {:d} 个处理器\n" #: ../src/sys/options/options_ini.cxx:200 #, c++-format @@ -126,39 +148,46 @@ msgid "" "\tEmpty key\n" "\tLine: {:s}" msgstr "" +"\t空键\n" +"\t行:{:s}" #: ../src/sys/optionsreader.cxx:127 #, c++-format msgid "\tEmpty key or value in command line '{:s}'\n" msgstr "" +"\t命令行中存在空键或空值 '{:s}'\n" -#: ../src/bout++.cxx:582 +#: ../src/bout++.cxx:587 #, c++-format msgid "\tExtra debug output {}\n" msgstr "" +"\t额外调试输出 {}\n" -#: ../src/bout++.cxx:561 +#: ../src/bout++.cxx:568 #, c++-format msgid "\tFFT support {}\n" msgstr "" +"\tFFT 支持 {}\n" -#: ../src/bout++.cxx:585 +#: ../src/bout++.cxx:590 #, c++-format msgid "\tField name tracking {}\n" msgstr "" +"\t字段名跟踪 {}\n" -#: ../src/bout++.cxx:583 +#: ../src/bout++.cxx:588 #, c++-format msgid "\tFloating-point exceptions {}\n" msgstr "" +"\t浮点异常 {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:440 +#: ../src/mesh/impls/bout/boutmesh.cxx:476 msgid "\tGrid size: " -msgstr "" +msgstr "\t网格大小:" -#: ../src/mesh/impls/bout/boutmesh.cxx:463 +#: ../src/mesh/impls/bout/boutmesh.cxx:499 msgid "\tGuard cells (x,y,z): " -msgstr "" +msgstr "\t保护单元 (x,y,z):" #: ../src/sys/options/options_ini.cxx:204 #, c++-format @@ -166,139 +195,159 @@ msgid "" "\tKey must not contain ':' character\n" "\tLine: {:s}" msgstr "" +"\t键中不能包含字符 ':'\n" +"\t行:{:s}" -#: ../src/bout++.cxx:563 +#: ../src/bout++.cxx:570 #, c++-format msgid "\tLAPACK support {}\n" msgstr "" +"\tLAPACK 支持 {}\n" -#: ../src/bout++.cxx:586 +#: ../src/bout++.cxx:591 #, c++-format msgid "\tMessage stack {}\n" msgstr "" +"\t消息栈 {}\n" -#: ../src/bout++.cxx:560 +#: ../src/bout++.cxx:567 #, c++-format msgid "\tMetrics mode is {}\n" msgstr "" +"\t度量模式为 {}\n" #: ../src/sys/optionsreader.cxx:111 #, c++-format msgid "\tMultiple '=' in command-line argument '{:s}'\n" msgstr "" +"\t命令行参数 '{:s}' 中包含多个 '='\n" -#: ../src/bout++.cxx:562 +#: ../src/bout++.cxx:569 #, c++-format msgid "\tNatural language support {}\n" msgstr "" +"\t自然语言支持 {}\n" -#: ../src/bout++.cxx:567 +#: ../src/bout++.cxx:574 #, c++-format msgid "\tNetCDF support {}{}\n" msgstr "" +"\tNetCDF 支持 {}{}\n" -#: ../src/bout++.cxx:577 +#: ../src/bout++.cxx:585 #, c++-format -msgid "\tOpenMP parallelisation {}" +msgid "\tOpenMP parallelisation {}, using {} threads\n" msgstr "" +"\tOpenMP 并行化 {},使用 {} 个线程\n" #. Mark the option as used #. Option not found -#: ../src/sys/options.cxx:311 ../src/sys/options.cxx:380 -#: ../src/sys/options.cxx:415 ../src/sys/options.cxx:457 -#: ../src/sys/options.cxx:717 ../src/sys/options.cxx:744 -#: ../src/sys/options.cxx:771 ../include/bout/options.hxx:516 -#: ../include/bout/options.hxx:549 ../include/bout/options.hxx:573 -#: ../include/bout/options.hxx:820 +#: ../include/bout/options.hxx:586 ../include/bout/options.hxx:619 +#: ../include/bout/options.hxx:643 ../include/bout/options.hxx:896 msgid "\tOption " msgstr "\t选项 " -#: ../src/sys/options.cxx:447 +#: ../src/sys/options.cxx:369 #, c++-format -msgid "\tOption '{:s}': Boolean expected. Got '{:s}'\n" -msgstr "" +msgid "\tOption {} = {}" +msgstr "\t选项 {} = {}" #: ../src/sys/options/options_ini.cxx:70 #, c++-format msgid "\tOptions file '{:s}' not found\n" msgstr "" +"\t未找到选项文件 '{:s}'\n" -#: ../src/bout++.cxx:568 +#: ../src/bout++.cxx:576 #, c++-format msgid "\tPETSc support {}\n" msgstr "" +"\tPETSc 支持 {}\n" -#: ../src/bout++.cxx:571 +#: ../src/bout++.cxx:579 #, c++-format msgid "\tPVODE support {}\n" msgstr "" +"\tPVODE 支持 {}\n" -#: ../src/bout++.cxx:557 +#: ../src/bout++.cxx:564 msgid "\tParallel NetCDF support disabled\n" msgstr "" +"\t并行 NetCDF 支持已禁用\n" -#: ../src/bout++.cxx:555 +#: ../src/bout++.cxx:562 msgid "\tParallel NetCDF support enabled\n" msgstr "" +"\t并行 NetCDF 支持已启用\n" -#: ../src/bout++.cxx:569 +#: ../src/bout++.cxx:577 #, c++-format msgid "\tPretty function name support {}\n" msgstr "" +"\t详细函数名支持 {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:437 +#: ../src/mesh/impls/bout/boutmesh.cxx:473 msgid "\tRead nz from input grid file\n" msgstr "" +"\t已从输入网格文件读取 nz\n" -#: ../src/mesh/mesh.cxx:238 +#: ../src/mesh/mesh.cxx:249 msgid "\tReading contravariant vector " -msgstr "" +msgstr "\t正在读取逆变向量 " -#: ../src/mesh/mesh.cxx:231 ../src/mesh/mesh.cxx:252 +#: ../src/mesh/mesh.cxx:242 ../src/mesh/mesh.cxx:263 msgid "\tReading covariant vector " -msgstr "" +msgstr "\t正在读取协变向量 " -#: ../src/bout++.cxx:548 +#: ../src/bout++.cxx:555 #, c++-format msgid "\tRuntime error checking {}" -msgstr "" +msgstr "\t运行时错误检查 {}" -#: ../src/bout++.cxx:573 +#: ../src/bout++.cxx:581 #, c++-format msgid "\tSLEPc support {}\n" msgstr "" +"\tSLEPc 支持 {}\n" -#: ../src/bout++.cxx:574 +#: ../src/bout++.cxx:582 #, c++-format msgid "\tSUNDIALS support {}\n" msgstr "" +"\tSUNDIALS 支持 {}\n" -#: ../src/bout++.cxx:572 +#: ../src/bout++.cxx:580 #, c++-format msgid "\tScore-P support {}\n" msgstr "" +"\tScore-P 支持 {}\n" -#: ../src/bout++.cxx:584 -#, fuzzy, c++-format +#: ../src/bout++.cxx:589 +#, c++-format msgid "\tSignal handling support {}\n" -msgstr "\t测试关掉\n" +msgstr "" +"\t信号处理支持 {}\n" #: ../src/solver/impls/split-rk/split-rk.cxx:76 #, c++-format msgid "\tUsing a timestep {:e}\n" msgstr "" +"\t使用时间步长 {:e}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:577 +#: ../src/mesh/impls/bout/boutmesh.cxx:613 msgid "\tdone\n" msgstr "" +"\t完成\n" #: ../src/solver/impls/split-rk/split-rk.cxx:41 msgid "" "\n" "\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n" msgstr "" +"\n" +"\tSplit Runge-Kutta-Legendre 与 SSP-RK3 求解器\n" -#: ../src/bout++.cxx:371 +#: ../src/bout++.cxx:378 msgid "" "\n" " -d \t\tLook in for input/output files\n" @@ -308,37 +357,47 @@ msgid "" " -v, --verbose\t\t\tIncrease verbosity\n" " -q, --quiet\t\t\tDecrease verbosity\n" msgstr "" +"\n" +" -d <数据目录>\t\t在 <数据目录> 中查找输入/输出文件\n" +" -f <选项文件名>\t\t使用 <选项文件名> 中给出的 OPTIONS\n" +" -o <设置文件名>\t将已使用的 OPTIONS 保存到 <选项文件名>\n" +" -l, --log <日志文件名>\t将日志输出到 <日志文件名>\n" +" -v, --verbose\t\t\t提高详细程度\n" +" -q, --quiet\t\t\t降低详细程度\n" -#: ../src/sys/expressionparser.cxx:302 +#: ../src/sys/expressionparser.cxx:341 #, c++-format msgid "" "\n" " {1: ^{2}}{0}\n" " Did you mean '{0}'?" msgstr "" +"\n" +" {1: ^{2}}{0}\n" +" 您是不是想写 '{0}'?" -#: ../src/solver/solver.cxx:580 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:586 +#, c++-format msgid "" "\n" "Run finished at : {:s}\n" msgstr "" "\n" -"计算结束于 {:s}\n" +"运行结束时间:{:s}\n" -#: ../src/solver/solver.cxx:532 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:540 +#, c++-format msgid "" "\n" "Run started at : {:s}\n" msgstr "" "\n" -"计算从 {:s} 开始\n" +"运行开始时间:{:s}\n" #. Raw string to help with the formatting of the message, and a #. separate variable so clang-format doesn't barf on the #. exception -#: ../src/sys/options.cxx:1102 +#: ../src/sys/options.cxx:1158 msgid "" "\n" "There were unused input options:\n" @@ -365,8 +424,30 @@ msgid "" "\n" "{}" msgstr "" +"\n" +"发现未使用的输入选项:\n" +"-----\n" +"{:i}\n" +"-----\n" +"有些选项可能被误拼写了。BOUT++ 的输入参数现在区分大小写,\n" +"并且其中一些名称已经改变。您可以尝试运行\n" +"\n" +" /bin/bout-v5-input-file-upgrader.py {}/{}\n" +"\n" +"以自动修复最常见的问题。如果上面的这些选项有时会根据其他\n" +"选项而被使用,您可以调用 `Options::setConditionallyUsed()`,例如:\n" +"\n" +" Options::root()[\"{}\"].setConditionallyUsed();\n" +"\n" +"将某个节或值标记为依赖于其他值,从而在这项检查中忽略它。或者,\n" +"如果您确信以上输入并非错误,可以设置\n" +"'input:error_on_unused_options=false' 来关闭这项未使用选项检查。\n" +"您也始终可以设置 'input:validate=true',在不运行完整模拟的情况下\n" +"检查输入。\n" +"\n" +"{}" -#: ../src/bout++.cxx:382 +#: ../src/bout++.cxx:389 #, c++-format msgid "" " --print-config\t\tPrint the compile-time configuration\n" @@ -398,117 +479,148 @@ msgid "" "For all possible input parameters, see the user manual and/or the physics " "model source (e.g. {:s}.cxx)\n" msgstr "" +" --print-config\t\t显示编译时配置\n" +" --list-solvers\t\t列出可用的时间求解器\n" +" --help-solver \t显示指定时间求解器的帮助\n" +" --list-laplacians\t\t列出可用的拉普拉斯反演求解器\n" +" --help-laplacian \t显示指定拉普拉斯反演求解器的帮助\n" +" --list-laplacexz\t\t列出可用的 LaplaceXZ 反演求解器\n" +" --help-laplacexz \t显示指定 LaplaceXZ 反演求解器的帮助\n" +" --list-invertpars\t\t列出可用的 InvertPar 求解器\n" +" --help-invertpar \t显示指定 InvertPar 求解器的帮助\n" +" --list-rkschemes\t\t列出可用的 Runge-Kutta 格式\n" +" --help-rkscheme \t显示指定 Runge-Kutta 格式的帮助\n" +" --list-meshes\t\t\t列出可用的 Mesh\n" +" --help-mesh \t\t显示指定 Mesh 的帮助\n" +" --list-xzinterpolations\t列出可用的 XZInterpolation\n" +" --help-xzinterpolation \t显示指定 XZInterpolation 的帮助\n" +" --list-zinterpolations\t列出可用的 ZInterpolation\n" +" --help-zinterpolation \t显示指定 ZInterpolation 的帮助\n" +" -h, --help\t\t\t此消息\n" +" restart [append]\t\t重新启动模拟。若指定 append,则追加到现有输出文件,否则覆盖它们\n" +" VAR=VALUE\t\t\t为输入参数 VAR 指定一个 VALUE\n" +"\n" +"有关所有可能的输入参数,请参阅用户手册和/或物理模型源码(例如 {:s}.cxx)\n" -#: ../src/bout++.cxx:379 +#: ../src/bout++.cxx:386 msgid " -c, --color\t\t\tColor output using bout-log-color\n" msgstr "" +" -c, --color\t\t\t使用 bout-log-color 输出彩色日志\n" -#: ../include/bout/options.hxx:823 +#: ../include/bout/options.hxx:899 msgid ") overwritten with:" -msgstr "" +msgstr ") 被覆盖为:" -#: ../src/bout++.cxx:550 +#: ../src/bout++.cxx:557 #, c++-format msgid ", level {}" -msgstr "" - -#: ../src/bout++.cxx:579 -#, c++-format -msgid ", using {} threads" -msgstr "" +msgstr ", 级别 {}" #: ../tests/unit/src/test_bout++.cxx:352 msgid "4 of 8" -msgstr "" +msgstr "4 / 8" -#: ../src/sys/options.cxx:868 +#: ../src/sys/options.cxx:895 msgid "All options used\n" msgstr "" +"所有选项均已使用\n" -#: ../src/bout++.cxx:528 +#: ../src/bout++.cxx:535 #, c++-format msgid "BOUT++ version {:s}\n" msgstr "" +"BOUT++ 版本 {:s}\n" -#: ../src/bout++.cxx:143 +#: ../src/bout++.cxx:147 msgid "Bad command line arguments:\n" msgstr "" +"错误的命令行参数:\n" + +#: ../src/sys/expressionparser.cxx:192 +#, c++-format +msgid "Boolean operator argument {:e} is not a bool" +msgstr "布尔运算符参数 {:e} 不是布尔值" -#: ../src/mesh/impls/bout/boutmesh.cxx:559 +#: ../src/mesh/impls/bout/boutmesh.cxx:595 msgid "Boundary regions in this processor: " -msgstr "" +msgstr "该处理器中的边界区域:" -#: ../src/mesh/impls/bout/boutmesh.cxx:355 +#: ../src/mesh/impls/bout/boutmesh.cxx:348 #, c++-format msgid "Cannot split {:d} X points equally between {:d} processors\n" msgstr "" +"无法将 {:d} 个 X 点平均分配给 {:d} 个处理器\n" -#: ../src/bout++.cxx:818 +#: ../src/bout++.cxx:829 msgid "Check if a file exists, and exit if it does." -msgstr "" +msgstr "检查文件是否存在,若存在则退出。" -#: ../src/bout++.cxx:533 -#, fuzzy, c++-format +#: ../src/bout++.cxx:540 +#, c++-format msgid "" "Code compiled on {:s} at {:s}\n" "\n" msgstr "" -"代码于 {:s} {:s} 编译\n" +"代码编译于 {:s},时间 {:s}\n" "\n" #: ../src/sys/optionsreader.cxx:130 msgid "Command line" -msgstr "" +msgstr "命令行" -#: ../src/bout++.cxx:544 ../tests/unit/src/test_bout++.cxx:358 +#: ../src/bout++.cxx:551 ../tests/unit/src/test_bout++.cxx:358 msgid "Compile-time options:\n" msgstr "" +"编译时选项:\n" #: ../tests/unit/src/test_bout++.cxx:362 msgid "Compiled with flags" -msgstr "" +msgstr "编译标志" -#: ../src/mesh/impls/bout/boutmesh.cxx:568 +#: ../src/mesh/impls/bout/boutmesh.cxx:604 msgid "Constructing default regions" -msgstr "" +msgstr "正在构建默认区域" -#: ../src/bout++.cxx:520 +#: ../src/bout++.cxx:527 #, c++-format msgid "Could not create PID file {:s}" -msgstr "" +msgstr "无法创建 PID 文件 {:s}" -#: ../src/mesh/impls/bout/boutmesh.cxx:318 +#: ../src/mesh/impls/bout/boutmesh.cxx:309 msgid "" "Could not find a valid value for NXPE. Try a different number of processors." -msgstr "" +msgstr "无法为 NXPE 找到有效值。请尝试使用不同数量的处理器。" #: ../src/sys/options/options_ini.cxx:160 #, c++-format msgid "Could not open output file '{:s}'\n" msgstr "" +"无法打开输出文件 '{:s}'\n" -#: ../src/bout++.cxx:652 +#: ../src/bout++.cxx:657 #, c++-format msgid "Could not open {:s}/{:s}.{:d} for writing" -msgstr "" +msgstr "无法打开 {:s}/{:s}.{:d} 进行写入" #. Error reading -#: ../src/mesh/mesh.cxx:532 +#: ../src/mesh/mesh.cxx:543 #, c++-format msgid "Could not read integer array '{:s}'\n" msgstr "" +"无法读取整数数组 '{:s}'\n" #. Failed . Probably not important enough to stop the simulation -#: ../src/bout++.cxx:632 +#: ../src/bout++.cxx:637 msgid "Could not run bout-log-color. Make sure it is in your PATH\n" msgstr "" +"无法运行 bout-log-color。请确认它在您的 PATH 中\n" -#: ../src/solver/solver.cxx:765 +#: ../src/solver/solver.cxx:772 #, c++-format msgid "Couldn't add Monitor: {:g} is not a multiple of {:g}!" -msgstr "" +msgstr "无法添加监视器:{:g} 不是 {:g} 的整数倍!" -#: ../src/sys/expressionparser.cxx:273 +#: ../src/sys/expressionparser.cxx:312 #, c++-format msgid "" "Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so " @@ -516,195 +628,216 @@ msgid "" "may need to change your input file.\n" "{}" msgstr "" +"无法找到生成器 '{}'。BOUT++ 表达式现在区分大小写,因此\n" +"您可能需要修改输入文件。\n" +"{}" -#: ../src/mesh/mesh.cxx:568 +#: ../src/mesh/mesh.cxx:587 #, c++-format msgid "Couldn't find region {:s} in regionMap2D" -msgstr "" +msgstr "无法在 regionMap2D 中找到区域 {:s}" -#: ../src/mesh/mesh.cxx:560 +#: ../src/mesh/mesh.cxx:571 ../src/mesh/mesh.cxx:579 #, c++-format msgid "Couldn't find region {:s} in regionMap3D" -msgstr "" +msgstr "无法在 regionMap3D 中找到区域 {:s}" -#: ../src/mesh/mesh.cxx:576 +#: ../src/mesh/mesh.cxx:595 #, c++-format msgid "Couldn't find region {:s} in regionMapPerp" -msgstr "" +msgstr "无法在 regionMapPerp 中找到区域 {:s}" #. Convert any exceptions to something a bit more useful -#: ../src/sys/options.cxx:336 +#: ../src/sys/options.cxx:361 #, c++-format msgid "Couldn't get {} from option {:s} = '{:s}': {}" -msgstr "" +msgstr "无法从选项 {1:s} = '{2:s}' 获取 {0}:{3}" -#: ../src/bout++.cxx:508 -#, fuzzy, c++-format +#: ../src/bout++.cxx:515 +#, c++-format msgid "DataDir \"{:s}\" does not exist or is not accessible\n" -msgstr "\"{:s}\" 不存在或不可访问\n" +msgstr "" +"数据目录 \"{:s}\" 不存在或不可访问\n" -#: ../src/bout++.cxx:505 -#, fuzzy, c++-format +#: ../src/bout++.cxx:512 +#, c++-format msgid "DataDir \"{:s}\" is not a directory\n" -msgstr "\"{:s}\" 不是目录\n" +msgstr "" +"数据目录 \"{:s}\" 不是目录\n" -#: ../src/solver/solver.cxx:665 +#: ../src/solver/solver.cxx:671 msgid "ERROR: Solver is already initialised\n" msgstr "" +"错误:求解器已经初始化\n" -#: ../src/bout++.cxx:209 -#, fuzzy, c++-format +#: ../src/bout++.cxx:216 +#, c++-format msgid "Error encountered during initialisation: {:s}\n" -msgstr "启动时遇到错误 : {:s}\n" +msgstr "" +"初始化期间发生错误:{:s}\n" -#: ../src/bout++.cxx:744 +#: ../src/bout++.cxx:751 msgid "Error whilst writing settings" -msgstr "" +msgstr "写入设置时发生错误" -#: ../src/mesh/impls/bout/boutmesh.cxx:332 +#: ../src/mesh/impls/bout/boutmesh.cxx:323 #, c++-format msgid "Error: nx must be greater than 2 times MXG (2 * {:d})" -msgstr "" +msgstr "错误:nx 必须大于 2 倍 MXG(2 * {:d})" -#: ../src/solver/solver.cxx:512 +#: ../src/solver/solver.cxx:520 msgid "Failed to initialise solver-> Aborting\n" msgstr "" +"求解器初始化失败 -> 中止\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:290 +#: ../src/mesh/impls/bout/boutmesh.cxx:281 #, c++-format msgid "Finding value for NXPE (ideal = {:f})\n" msgstr "" +"正在查找 NXPE 的值(理想值 = {:f})\n" -#: ../src/solver/solver.cxx:668 +#: ../src/solver/solver.cxx:674 msgid "Initialising solver\n" msgstr "" +"正在初始化求解器\n" -#: ../src/bout++.cxx:494 +#: ../src/bout++.cxx:501 msgid "" "Input and output file for settings must be different.\n" "Provide -o to avoid this issue.\n" msgstr "" +"设置的输入文件和输出文件必须不同。\n" +"请提供 -o 以避免此问题。\n" #: ../src/sys/optionsreader.cxx:76 msgid "Invalid command line option '-' found - maybe check whitespace?" -msgstr "" +msgstr "发现无效的命令行选项 '-',也许应检查空白字符?" -#: ../src/mesh/impls/bout/boutmesh.cxx:400 +#: ../src/mesh/impls/bout/boutmesh.cxx:436 msgid "Loading mesh" -msgstr "" +msgstr "正在加载网格" -#: ../src/mesh/impls/bout/boutmesh.cxx:415 +#: ../src/mesh/impls/bout/boutmesh.cxx:451 msgid "Mesh must contain nx" -msgstr "" +msgstr "网格必须包含 nx" -#: ../src/mesh/impls/bout/boutmesh.cxx:419 +#: ../src/mesh/impls/bout/boutmesh.cxx:455 msgid "Mesh must contain ny" -msgstr "" +msgstr "网格必须包含 ny" #. Not found -#: ../src/mesh/mesh.cxx:536 +#: ../src/mesh/mesh.cxx:547 #, c++-format msgid "Missing integer array {:s}\n" msgstr "" +"缺少整数数组 {:s}\n" -#: ../src/solver/solver.cxx:905 +#: ../src/solver/solver.cxx:911 #, c++-format msgid "Monitor signalled to quit (exception {})\n" msgstr "" +"监视器发出退出信号(异常 {})\n" -#: ../src/solver/solver.cxx:883 +#: ../src/solver/solver.cxx:889 #, c++-format msgid "Monitor signalled to quit (return code {})" -msgstr "" +msgstr "监视器发出退出信号(返回码 {})" -#: ../src/bout++.cxx:823 +#: ../src/bout++.cxx:834 msgid "Name of file whose existence triggers a stop" -msgstr "" +msgstr "触发停止的文件名" -#: ../src/mesh/impls/bout/boutmesh.cxx:565 +#: ../src/mesh/impls/bout/boutmesh.cxx:601 msgid "No boundary regions in this processor" -msgstr "" +msgstr "该处理器中没有边界区域" -#: ../src/mesh/impls/bout/boutmesh.cxx:550 +#: ../src/mesh/impls/bout/boutmesh.cxx:586 msgid "No boundary regions; domain is periodic\n" msgstr "" +"没有边界区域;区域是周期性的\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:254 +#: ../src/mesh/impls/bout/boutmesh.cxx:245 #, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n" msgstr "" +"处理器数量 ({:d}) 不能被 x 方向上的 NP 数 ({:d}) 整除\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:267 +#: ../src/mesh/impls/bout/boutmesh.cxx:258 #, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n" msgstr "" +"处理器数量 ({:d}) 不能被 y 方向上的 NP 数 ({:d}) 整除\n" #. Less than 2 time-steps left -#: ../src/bout++.cxx:896 +#: ../src/bout++.cxx:908 #, c++-format msgid "Only {:e} seconds ({:.2f} steps) left. Quitting\n" msgstr "" +"只剩 {:e} 秒({:.2f} 步)。正在退出\n" -#: ../src/sys/options.cxx:303 ../src/sys/options.cxx:345 -#: ../src/sys/options.cxx:393 ../src/sys/options.cxx:428 -#: ../src/sys/options.cxx:703 ../src/sys/options.cxx:730 -#: ../src/sys/options.cxx:757 +#: ../src/sys/options.cxx:382 ../src/sys/options.cxx:398 +#: ../src/sys/options.cxx:441 ../src/sys/options.cxx:471 +#: ../src/sys/options.cxx:745 ../src/sys/options.cxx:767 +#: ../src/sys/options.cxx:789 #, c++-format msgid "Option {:s} has no value" -msgstr "" +msgstr "选项 {:s} 没有值" #. Doesn't exist -#: ../src/sys/options.cxx:159 +#: ../src/sys/options.cxx:172 #, c++-format msgid "Option {:s}:{:s} does not exist" -msgstr "" +msgstr "选项 {:s}:{:s} 不存在" -#: ../include/bout/options.hxx:828 +#: ../include/bout/options.hxx:904 #, c++-format msgid "" "Options: Setting a value from same source ({:s}) to new value '{:s}' - old " "value was '{:s}'." -msgstr "" +msgstr "选项:正在将来自同一来源 ({:s}) 的值设置为新值 '{:s}',旧值为 '{:s}'。" -#: ../src/mesh/impls/bout/boutmesh.cxx:552 +#: ../src/mesh/impls/bout/boutmesh.cxx:588 msgid "Possible boundary regions are: " -msgstr "" +msgstr "可能的边界区域有:" -#: ../src/bout++.cxx:538 +#: ../src/bout++.cxx:545 #, c++-format msgid "" "Processor number: {:d} of {:d}\n" "\n" msgstr "" +"处理器编号:{:d} / {:d}\n" +"\n" -#: ../src/mesh/mesh.cxx:609 +#: ../src/mesh/mesh.cxx:642 #, c++-format msgid "Registered region 2D {:s}" -msgstr "" +msgstr "已注册 2D 区域 {:s}" -#: ../src/mesh/mesh.cxx:599 +#: ../src/mesh/mesh.cxx:632 #, c++-format msgid "Registered region 3D {:s}" -msgstr "" +msgstr "已注册 3D 区域 {:s}" -#: ../src/mesh/mesh.cxx:619 +#: ../src/mesh/mesh.cxx:652 #, c++-format msgid "Registered region Perp {:s}" -msgstr "" +msgstr "已注册 Perp 区域 {:s}" -#: ../src/bout++.cxx:529 +#: ../src/bout++.cxx:536 #, c++-format msgid "Revision: {:s}\n" msgstr "" +"修订版本:{:s}\n" -#: ../src/solver/solver.cxx:581 +#: ../src/solver/solver.cxx:587 msgid "Run time : " msgstr "计算时间" #. / Run the solver -#: ../src/solver/solver.cxx:525 +#: ../src/solver/solver.cxx:533 msgid "" "Running simulation\n" "\n" @@ -714,66 +847,73 @@ msgstr "" #: ../tests/unit/src/test_bout++.cxx:359 msgid "Signal" -msgstr "" +msgstr "信号" -#: ../src/bout++.cxx:865 +#: ../src/bout++.cxx:876 msgid "" "Sim Time | RHS evals | Wall Time | Calc Inv Comm I/O SOLVER\n" "\n" msgstr "" +"模拟时间 | RHS 次数 | 墙钟时间 | 计算 反演 通信 I/O 求解器\n" +"\n" -#: ../src/bout++.cxx:868 +#: ../src/bout++.cxx:879 msgid "" "Sim Time | RHS_e evals | RHS_I evals | Wall Time | Calc Inv " "Comm I/O SOLVER\n" "\n" msgstr "" +"模拟时间 | RHS_e 次数 | RHS_I 次数 | 墙钟时间 | 计算 反演 通信 I/O 求解器\n" +"\n" -#: ../src/solver/solver.cxx:506 +#: ../src/solver/solver.cxx:514 #, c++-format msgid "Solver running for {:d} outputs with monitor timestep of {:e}\n" msgstr "" +"求解器正在运行 {:d} 个输出,监视步长为 {:e}\n" -#: ../src/solver/solver.cxx:502 +#: ../src/solver/solver.cxx:510 #, c++-format msgid "Solver running for {:d} outputs with output timestep of {:e}\n" msgstr "" +"求解器正在运行 {:d} 个输出,输出步长为 {:e}\n" -#: ../src/solver/solver.cxx:781 +#: ../src/solver/solver.cxx:788 #, c++-format msgid "" "Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) after init is " "called!" -msgstr "" +msgstr "Solver::addMonitor:在调用 init 之后无法将时间步长从 {:g} 减少到 {:g}!" -#: ../src/solver/solver.cxx:1281 +#: ../src/solver/solver.cxx:1289 #, c++-format msgid "" "Time derivative at wrong location - Field is at {:s}, derivative is at {:s} " "for field '{:s}'\n" msgstr "" +"时间导数位于错误位置 - 字段位于 {:s},导数位于 {:s},对应字段 '{:s}'\n" -#: ../src/solver/solver.cxx:1480 +#: ../src/solver/solver.cxx:1494 #, c++-format msgid "Time derivative for variable '{:s}' not set" -msgstr "" +msgstr "变量 '{:s}' 的时间导数尚未设置" -#: ../src/mesh/mesh.cxx:605 +#: ../src/mesh/mesh.cxx:638 #, c++-format msgid "Trying to add an already existing region {:s} to regionMap2D" -msgstr "" +msgstr "试图将已存在的区域 {:s} 添加到 regionMap2D" -#: ../src/mesh/mesh.cxx:595 +#: ../src/mesh/mesh.cxx:614 #, c++-format msgid "Trying to add an already existing region {:s} to regionMap3D" -msgstr "" +msgstr "试图将已存在的区域 {:s} 添加到 regionMap3D" -#: ../src/mesh/mesh.cxx:616 +#: ../src/mesh/mesh.cxx:649 #, c++-format msgid "Trying to add an already existing region {:s} to regionMapPerp" -msgstr "" +msgstr "试图将已存在的区域 {:s} 添加到 regionMapPerp" -#: ../src/sys/options.cxx:99 ../src/sys/options.cxx:138 +#: ../src/sys/options.cxx:112 ../src/sys/options.cxx:151 #, c++-format msgid "" "Trying to index Option '{0}' with '{1}', but '{0}' is a value, not a " @@ -781,156 +921,179 @@ msgid "" "This is likely the result of clashing input options, and you may have to " "rename one of them.\n" msgstr "" +"正在尝试用 '{1}' 索引选项 '{0}',但 '{0}' 是一个值,而不是一个节。\n" +"这很可能是输入选项冲突导致的,您可能需要重命名其中之一。\n" -#: ../src/mesh/coordinates.cxx:1462 +#: ../src/mesh/coordinates.cxx:1464 msgid "" "Unrecognised paralleltransform option.\n" "Valid choices are 'identity', 'shifted', 'fci'" msgstr "" +"无法识别的 paralleltransform 选项。\n" +"有效选项为 'identity'、'shifted'、'fci'" -#: ../src/sys/options.cxx:872 +#: ../src/sys/options.cxx:899 msgid "Unused options:\n" msgstr "" +"未使用的选项:\n" -#: ../src/bout++.cxx:439 +#: ../src/bout++.cxx:446 #, c++-format msgid "Usage is {:s} -d \n" msgstr "" +"用法:{:s} -d <数据目录>\n" -#: ../src/bout++.cxx:448 +#: ../src/bout++.cxx:455 #, c++-format msgid "Usage is {:s} -f \n" msgstr "" +"用法:{:s} -f <选项文件名>\n" -#: ../src/bout++.cxx:466 +#: ../src/bout++.cxx:473 #, c++-format msgid "Usage is {:s} -l \n" msgstr "" +"用法:{:s} -l <日志文件名>\n" -#: ../src/bout++.cxx:457 +#: ../src/bout++.cxx:464 #, c++-format msgid "Usage is {:s} -o \n" msgstr "" +"用法:{:s} -o <设置文件名>\n" -#: ../src/bout++.cxx:353 +#: ../src/bout++.cxx:360 #, c++-format msgid "Usage is {} {} \n" msgstr "" +"用法:{} {} <名称>\n" #: ../tests/unit/src/test_bout++.cxx:32 ../tests/unit/src/test_bout++.cxx:46 msgid "Usage:" -msgstr "" +msgstr "用法:" #. Print help message -- note this will be displayed once per processor as we've not #. started MPI yet. -#: ../src/bout++.cxx:367 +#: ../src/bout++.cxx:374 #, c++-format msgid "" "Usage: {:s} [-d ] [-f ] [restart [append]] " "[VAR=VALUE]\n" msgstr "" +"用法:{:s} [-d <数据目录>] [-f <选项文件名>] [restart [append]] [VAR=VALUE]\n" #. restart file should be written by physics model -#: ../src/solver/solver.cxx:921 +#: ../src/solver/solver.cxx:927 msgid "User signalled to quit. Returning\n" msgstr "" +"用户发出退出信号。正在返回\n" -#: ../src/sys/options.cxx:373 +#: ../src/sys/options.cxx:486 +#, c++-format +msgid "Value for option {:s} = {:e} is not a bool" +msgstr "选项 {:s} = {:e} 的值不是布尔值" + +#: ../src/sys/options.cxx:426 #, c++-format msgid "Value for option {:s} = {:e} is not an integer" -msgstr "" +msgstr "选项 {:s} = {:e} 的值不是整数" -#: ../src/sys/options.cxx:408 +#: ../src/sys/options.cxx:456 #, c++-format msgid "Value for option {:s} cannot be converted to a BoutReal" -msgstr "" +msgstr "选项 {:s} 的值无法转换为 BoutReal" -#: ../src/sys/options.cxx:581 +#: ../src/sys/options.cxx:623 #, c++-format msgid "Value for option {:s} cannot be converted to a Field2D" -msgstr "" +msgstr "选项 {:s} 的值无法转换为 Field2D" -#: ../src/sys/options.cxx:529 +#: ../src/sys/options.cxx:571 #, c++-format msgid "Value for option {:s} cannot be converted to a Field3D" -msgstr "" +msgstr "选项 {:s} 的值无法转换为 Field3D" -#: ../src/sys/options.cxx:663 +#: ../src/sys/options.cxx:705 #, c++-format msgid "Value for option {:s} cannot be converted to a FieldPerp" -msgstr "" +msgstr "选项 {:s} 的值无法转换为 FieldPerp" -#: ../src/sys/options.cxx:451 +#: ../src/sys/options.cxx:491 #, c++-format msgid "Value for option {:s} cannot be converted to a bool" -msgstr "" +msgstr "选项 {:s} 的值无法转换为 bool" -#: ../src/sys/options.cxx:709 +#: ../src/sys/options.cxx:751 #, c++-format msgid "Value for option {:s} cannot be converted to an Array" -msgstr "" +msgstr "选项 {:s} 的值无法转换为 Array" -#: ../src/sys/options.cxx:736 +#: ../src/sys/options.cxx:773 #, c++-format msgid "Value for option {:s} cannot be converted to an Matrix" -msgstr "" +msgstr "选项 {:s} 的值无法转换为 Matrix" -#: ../src/sys/options.cxx:763 +#: ../src/sys/options.cxx:795 #, c++-format msgid "Value for option {:s} cannot be converted to an Tensor" -msgstr "" +msgstr "选项 {:s} 的值无法转换为 Tensor" #. Another type which can't be converted -#: ../src/sys/options.cxx:365 +#: ../src/sys/options.cxx:418 #, c++-format msgid "Value for option {:s} is not an integer" -msgstr "" +msgstr "选项 {:s} 的值不是整数" -#: ../src/solver/solver.cxx:1232 ../src/solver/solver.cxx:1238 +#: ../src/solver/solver.cxx:1240 ../src/solver/solver.cxx:1246 #, c++-format msgid "Variable '{:s}' not initialised" -msgstr "" +msgstr "变量 '{:s}' 尚未初始化" -#: ../src/mesh/impls/bout/boutmesh.cxx:431 +#: ../src/mesh/impls/bout/boutmesh.cxx:467 #, c++-format msgid "" "WARNING: Number of toroidal points should be 2^n for efficient FFT " "performance -- consider changing MZ ({:d}) if using FFTs\n" msgstr "" +"警告:为了获得高效的 FFT 性能,环向点数应为 2^n;如果使用 FFT,请考虑修改 MZ ({:d})\n" -#: ../src/mesh/coordinates.cxx:633 +#: ../src/mesh/coordinates.cxx:635 msgid "WARNING: extrapolating input mesh quantities into x-boundary cells\n" msgstr "" +"警告:正在将输入网格量外推到 x 边界单元\n" -#: ../src/mesh/coordinates.cxx:410 +#: ../src/mesh/coordinates.cxx:412 msgid "" "WARNING: extrapolating input mesh quantities into x-boundary cells. Set " "option extrapolate_x=false to disable this.\n" msgstr "" +"警告:正在将输入网格量外推到 x 边界单元。设置选项 extrapolate_x=false 可禁用此行为。\n" -#: ../src/mesh/coordinates.cxx:638 +#: ../src/mesh/coordinates.cxx:640 msgid "WARNING: extrapolating input mesh quantities into y-boundary cells\n" msgstr "" +"警告:正在将输入网格量外推到 y 边界单元\n" -#: ../src/mesh/coordinates.cxx:415 +#: ../src/mesh/coordinates.cxx:417 msgid "" "WARNING: extrapolating input mesh quantities into y-boundary cells. Set " "option extrapolate_y=false to disable this.\n" msgstr "" +"警告:正在将输入网格量外推到 y 边界单元。设置选项 extrapolate_y=false 可禁用此行为。\n" -#: ../src/bout++.cxx:814 +#: ../src/bout++.cxx:825 msgid "Wall time limit in hours. By default (< 0), no limit" -msgstr "" +msgstr "墙钟时间限制(小时)。默认值(< 0)表示不限制" #: ../src/sys/optionsreader.cxx:42 #, c++-format msgid "Writing options to file {:s}\n" msgstr "" +"正在将选项写入文件 {:s}\n" #. / The source label given to default values -#: ../src/sys/options.cxx:15 +#: ../src/sys/options.cxx:34 msgid "default" -msgstr "" +msgstr "默认" #~ msgid "\tChecking disabled\n" #~ msgstr "\t测试关掉\n" @@ -939,9 +1102,5 @@ msgstr "" #~ msgid "\tChecking enabled, level {:d}\n" #~ msgstr "\t测试打开,级别 {:d}\n" -#, fuzzy -#~ msgid "Option {:s} is not a section" -#~ msgstr "\"{:s}\" 不是目录\n" - #~ msgid "Error encountered during initialisation\n" #~ msgstr "启动时遇到错误\n" diff --git a/locale/zh_TW/libbout.po b/locale/zh_TW/libbout.po index de0230d68d..8b79978d95 100644 --- a/locale/zh_TW/libbout.po +++ b/locale/zh_TW/libbout.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: BOUT++ 4.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-12 09:17+0100\n" +"POT-Creation-Date: 2025-08-13 23:37+0100\n" "PO-Revision-Date: 2018-10-22 22:56+0100\n" "Last-Translator: \n" "Language-Team: Chinese (traditional)\n" @@ -16,109 +16,131 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:191 +#: ../src/mesh/impls/bout/boutmesh.cxx:182 #, c++-format msgid "" "\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" +"\t -> Core 區域 jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) 必須是 MYSUB ({:d}) 的整數倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:224 +#: ../src/mesh/impls/bout/boutmesh.cxx:215 #, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" +"\t -> Core 區域 jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) 必須是 MYSUB ({:d}) 的整數倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:199 +#: ../src/mesh/impls/bout/boutmesh.cxx:190 #, c++-format msgid "" "\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must be a multiple " "of MYSUB ({:d})\n" msgstr "" +"\t -> Core 區域 jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) 必須是 MYSUB ({:d}) 的整數倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:309 +#: ../src/mesh/impls/bout/boutmesh.cxx:300 msgid "\t -> Good value\n" -msgstr "\t -> 好的號碼\n" +msgstr "" +"\t -> 好的號碼\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:180 +#: ../src/mesh/impls/bout/boutmesh.cxx:171 #, c++-format msgid "" "\t -> Leg region jyseps1_1+1 ({:d}) must be a multiple of MYSUB ({:d})\n" msgstr "" +"\t -> Leg 區域 jyseps1_1+1 ({:d}) 必須是 MYSUB ({:d}) 的整數倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:215 +#: ../src/mesh/impls/bout/boutmesh.cxx:206 #, c++-format msgid "" "\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" +"\t -> leg 區域 jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) 必須是 MYSUB ({:d}) 的整數倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:232 +#: ../src/mesh/impls/bout/boutmesh.cxx:223 #, c++-format msgid "" "\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a multiple of " "MYSUB ({:d})\n" msgstr "" +"\t -> leg 區域 ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) 必須是 MYSUB ({:d}) 的整數倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:208 +#: ../src/mesh/impls/bout/boutmesh.cxx:199 #, c++-format msgid "" "\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must be a " "multiple of MYSUB ({:d})\n" msgstr "" +"\t -> leg 區域 ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) 必須是 MYSUB ({:d}) 的整數倍\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:175 +#: ../src/mesh/impls/bout/boutmesh.cxx:166 #, c++-format msgid "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n" msgstr "" +"\t -> ny/NYPE ({:d}/{:d} = {:d}) 必須 >= MYG ({:d})\n" #: ../src/bout++.cxx:575 #, c++-format +msgid "\tADIOS2 support {}\n" +msgstr "" +"\tADIOS2 支援 {}\n" + +#: ../src/bout++.cxx:583 +#, c++-format msgid "\tBacktrace in exceptions {}\n" msgstr "" +"\t例外中的回溯 {}\n" #. Loop over all possibilities #. Processors divide equally #. Mesh in X divides equally #. Mesh in Y divides equally -#: ../src/mesh/impls/bout/boutmesh.cxx:297 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:288 +#, c++-format msgid "\tCandidate value: {:d}\n" -msgstr "\t候選人數目 {:d}\n" +msgstr "" +"\t候選值:{:d}\n" -#: ../src/bout++.cxx:576 +#: ../src/bout++.cxx:584 #, c++-format msgid "\tColour in logs {}\n" msgstr "" +"\t日誌著色 {}\n" -#: ../src/bout++.cxx:594 +#: ../src/bout++.cxx:599 msgid "\tCommand line options for this run : " -msgstr "" +msgstr "\t本次執行的命令列選項:" #. The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings #. which could cause problems (e.g. terminate strings). -#: ../src/bout++.cxx:590 -#, fuzzy, c++-format +#: ../src/bout++.cxx:595 +#, c++-format msgid "\tCompiled with flags : {:s}\n" -msgstr "\t用設置編譯: {:s}\n" +msgstr "" +"\t編譯旗標:{:s}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:324 -#, fuzzy, c++-format +#: ../src/mesh/impls/bout/boutmesh.cxx:315 +#, c++-format msgid "" "\tDomain split (NXPE={:d}, NYPE={:d}) into domains (localNx={:d}, " "localNy={:d})\n" -msgstr "\t域 (NXPE={:d}, NYPE={:d}) 分裂成域 (localNx={:d}, localNy={:d})\n" +msgstr "" +"\t網域已拆分為 (NXPE={:d}, NYPE={:d}),局部網域為 (localNx={:d}, localNy={:d})\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:364 +#: ../src/mesh/impls/bout/boutmesh.cxx:357 #, c++-format msgid "\tERROR: Cannot split {:d} Y points equally between {:d} processors\n" msgstr "" +"\t錯誤:無法將 {:d} 個 Y 點平均分配給 {:d} 個處理器\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:372 +#: ../src/mesh/impls/bout/boutmesh.cxx:365 #, c++-format msgid "\tERROR: Cannot split {:d} Z points equally between {:d} processors\n" msgstr "" +"\t錯誤:無法將 {:d} 個 Z 點平均分配給 {:d} 個處理器\n" #: ../src/sys/options/options_ini.cxx:200 #, c++-format @@ -126,39 +148,46 @@ msgid "" "\tEmpty key\n" "\tLine: {:s}" msgstr "" +"\t空鍵\n" +"\t行:{:s}" #: ../src/sys/optionsreader.cxx:127 -#, fuzzy, c++-format +#, c++-format msgid "\tEmpty key or value in command line '{:s}'\n" -msgstr "\t命令行中的空鍵或值 '{:s}'\n" +msgstr "" +"\t命令列中存在空鍵或空值 '{:s}'\n" -#: ../src/bout++.cxx:582 +#: ../src/bout++.cxx:587 #, c++-format msgid "\tExtra debug output {}\n" msgstr "" +"\t額外除錯輸出 {}\n" -#: ../src/bout++.cxx:561 +#: ../src/bout++.cxx:568 #, c++-format msgid "\tFFT support {}\n" msgstr "" +"\tFFT 支援 {}\n" -#: ../src/bout++.cxx:585 +#: ../src/bout++.cxx:590 #, c++-format msgid "\tField name tracking {}\n" msgstr "" +"\t欄位名稱追蹤 {}\n" -#: ../src/bout++.cxx:583 +#: ../src/bout++.cxx:588 #, c++-format msgid "\tFloating-point exceptions {}\n" msgstr "" +"\t浮點例外 {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:440 +#: ../src/mesh/impls/bout/boutmesh.cxx:476 msgid "\tGrid size: " msgstr "\t網格大小: " -#: ../src/mesh/impls/bout/boutmesh.cxx:463 +#: ../src/mesh/impls/bout/boutmesh.cxx:499 msgid "\tGuard cells (x,y,z): " -msgstr "" +msgstr "\t保護單元 (x,y,z):" #: ../src/sys/options/options_ini.cxx:204 #, c++-format @@ -166,140 +195,159 @@ msgid "" "\tKey must not contain ':' character\n" "\tLine: {:s}" msgstr "" +"\t鍵中不可包含字元 ':'\n" +"\t行:{:s}" -#: ../src/bout++.cxx:563 +#: ../src/bout++.cxx:570 #, c++-format msgid "\tLAPACK support {}\n" msgstr "" +"\tLAPACK 支援 {}\n" -#: ../src/bout++.cxx:586 +#: ../src/bout++.cxx:591 #, c++-format msgid "\tMessage stack {}\n" msgstr "" +"\t訊息堆疊 {}\n" -#: ../src/bout++.cxx:560 +#: ../src/bout++.cxx:567 #, c++-format msgid "\tMetrics mode is {}\n" msgstr "" +"\t度量模式為 {}\n" #: ../src/sys/optionsreader.cxx:111 #, c++-format msgid "\tMultiple '=' in command-line argument '{:s}'\n" msgstr "" +"\t命令列參數 '{:s}' 中包含多個 '='\n" -#: ../src/bout++.cxx:562 +#: ../src/bout++.cxx:569 #, c++-format msgid "\tNatural language support {}\n" msgstr "" +"\t自然語言支援 {}\n" -#: ../src/bout++.cxx:567 -#, fuzzy, c++-format +#: ../src/bout++.cxx:574 +#, c++-format msgid "\tNetCDF support {}{}\n" -msgstr "\tOpenMP並行化已禁用\n" +msgstr "" +"\tNetCDF 支援 {}{}\n" -#: ../src/bout++.cxx:577 -#, fuzzy, c++-format -msgid "\tOpenMP parallelisation {}" -msgstr "\tOpenMP並行化已禁用\n" +#: ../src/bout++.cxx:585 +#, c++-format +msgid "\tOpenMP parallelisation {}, using {} threads\n" +msgstr "" +"\tOpenMP 平行化 {},使用 {} 個執行緒\n" #. Mark the option as used #. Option not found -#: ../src/sys/options.cxx:311 ../src/sys/options.cxx:380 -#: ../src/sys/options.cxx:415 ../src/sys/options.cxx:457 -#: ../src/sys/options.cxx:717 ../src/sys/options.cxx:744 -#: ../src/sys/options.cxx:771 ../include/bout/options.hxx:516 -#: ../include/bout/options.hxx:549 ../include/bout/options.hxx:573 -#: ../include/bout/options.hxx:820 +#: ../include/bout/options.hxx:586 ../include/bout/options.hxx:619 +#: ../include/bout/options.hxx:643 ../include/bout/options.hxx:896 msgid "\tOption " msgstr "\t選項 " -#: ../src/sys/options.cxx:447 -#, fuzzy, c++-format -msgid "\tOption '{:s}': Boolean expected. Got '{:s}'\n" -msgstr "\t選項 '{:s}': 布爾預期. 拿到 '{:s}'\n" +#: ../src/sys/options.cxx:369 +#, c++-format +msgid "\tOption {} = {}" +msgstr "\t選項 {} = {}" #: ../src/sys/options/options_ini.cxx:70 -#, fuzzy, c++-format +#, c++-format msgid "\tOptions file '{:s}' not found\n" -msgstr "\t找不到選項文件 '{:s}'\n" +msgstr "" +"\t找不到選項檔案 '{:s}'\n" -#: ../src/bout++.cxx:568 +#: ../src/bout++.cxx:576 #, c++-format msgid "\tPETSc support {}\n" msgstr "" +"\tPETSc 支援 {}\n" -#: ../src/bout++.cxx:571 +#: ../src/bout++.cxx:579 #, c++-format msgid "\tPVODE support {}\n" msgstr "" +"\tPVODE 支援 {}\n" -#: ../src/bout++.cxx:557 -#, fuzzy +#: ../src/bout++.cxx:564 msgid "\tParallel NetCDF support disabled\n" -msgstr "\tOpenMP並行化已禁用\n" +msgstr "" +"\t平行 NetCDF 支援已停用\n" -#: ../src/bout++.cxx:555 +#: ../src/bout++.cxx:562 msgid "\tParallel NetCDF support enabled\n" msgstr "" +"\t平行 NetCDF 支援已啟用\n" -#: ../src/bout++.cxx:569 +#: ../src/bout++.cxx:577 #, c++-format msgid "\tPretty function name support {}\n" msgstr "" +"\t詳細函式名稱支援 {}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:437 +#: ../src/mesh/impls/bout/boutmesh.cxx:473 msgid "\tRead nz from input grid file\n" msgstr "" +"\t已從輸入網格檔案讀取 nz\n" -#: ../src/mesh/mesh.cxx:238 +#: ../src/mesh/mesh.cxx:249 msgid "\tReading contravariant vector " -msgstr "" +msgstr "\t正在讀取逆變向量 " -#: ../src/mesh/mesh.cxx:231 ../src/mesh/mesh.cxx:252 +#: ../src/mesh/mesh.cxx:242 ../src/mesh/mesh.cxx:263 msgid "\tReading covariant vector " -msgstr "" +msgstr "\t正在讀取協變向量 " -#: ../src/bout++.cxx:548 +#: ../src/bout++.cxx:555 #, c++-format msgid "\tRuntime error checking {}" -msgstr "" +msgstr "\t執行期錯誤檢查 {}" -#: ../src/bout++.cxx:573 +#: ../src/bout++.cxx:581 #, c++-format msgid "\tSLEPc support {}\n" msgstr "" +"\tSLEPc 支援 {}\n" -#: ../src/bout++.cxx:574 +#: ../src/bout++.cxx:582 #, c++-format msgid "\tSUNDIALS support {}\n" msgstr "" +"\tSUNDIALS 支援 {}\n" -#: ../src/bout++.cxx:572 +#: ../src/bout++.cxx:580 #, c++-format msgid "\tScore-P support {}\n" msgstr "" +"\tScore-P 支援 {}\n" -#: ../src/bout++.cxx:584 -#, fuzzy, c++-format +#: ../src/bout++.cxx:589 +#, c++-format msgid "\tSignal handling support {}\n" -msgstr "\t測試關掉\n" +msgstr "" +"\t訊號處理支援 {}\n" #: ../src/solver/impls/split-rk/split-rk.cxx:76 #, c++-format msgid "\tUsing a timestep {:e}\n" msgstr "" +"\t使用時間步長 {:e}\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:577 +#: ../src/mesh/impls/bout/boutmesh.cxx:613 msgid "\tdone\n" -msgstr "\t完\n" +msgstr "" +"\t完\n" #: ../src/solver/impls/split-rk/split-rk.cxx:41 msgid "" "\n" "\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n" msgstr "" +"\n" +"\tSplit Runge-Kutta-Legendre 與 SSP-RK3 求解器\n" -#: ../src/bout++.cxx:371 +#: ../src/bout++.cxx:378 msgid "" "\n" " -d \t\tLook in for input/output files\n" @@ -309,37 +357,47 @@ msgid "" " -v, --verbose\t\t\tIncrease verbosity\n" " -q, --quiet\t\t\tDecrease verbosity\n" msgstr "" +"\n" +" -d <資料目錄>\t\t在 <資料目錄> 中尋找輸入/輸出檔案\n" +" -f <選項檔名>\t\t使用 <選項檔名> 中給出的 OPTIONS\n" +" -o <設定檔名>\t將已使用的 OPTIONS 儲存到 <選項檔名>\n" +" -l, --log <日誌檔名>\t將日誌輸出到 <日誌檔名>\n" +" -v, --verbose\t\t\t提高詳細程度\n" +" -q, --quiet\t\t\t降低詳細程度\n" -#: ../src/sys/expressionparser.cxx:302 +#: ../src/sys/expressionparser.cxx:341 #, c++-format msgid "" "\n" " {1: ^{2}}{0}\n" " Did you mean '{0}'?" msgstr "" +"\n" +" {1: ^{2}}{0}\n" +" 您是不是想寫 '{0}'?" -#: ../src/solver/solver.cxx:580 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:586 +#, c++-format msgid "" "\n" "Run finished at : {:s}\n" msgstr "" "\n" -"计算结束于 {:s}\n" +"執行結束時間:{:s}\n" -#: ../src/solver/solver.cxx:532 -#, fuzzy, c++-format +#: ../src/solver/solver.cxx:540 +#, c++-format msgid "" "\n" "Run started at : {:s}\n" msgstr "" "\n" -"计算从 {:s} 开始\n" +"執行開始時間:{:s}\n" #. Raw string to help with the formatting of the message, and a #. separate variable so clang-format doesn't barf on the #. exception -#: ../src/sys/options.cxx:1102 +#: ../src/sys/options.cxx:1158 msgid "" "\n" "There were unused input options:\n" @@ -366,8 +424,30 @@ msgid "" "\n" "{}" msgstr "" +"\n" +"發現未使用的輸入選項:\n" +"-----\n" +"{:i}\n" +"-----\n" +"有些選項可能被拼錯了。BOUT++ 的輸入參數現在區分大小寫,\n" +"而且其中有些名稱已經改變。您可以嘗試執行\n" +"\n" +" /bin/bout-v5-input-file-upgrader.py {}/{}\n" +"\n" +"來自動修正常見問題。如果上面的這些選項有時會依賴其他選項\n" +"才被使用,您可以呼叫 `Options::setConditionallyUsed()`,例如:\n" +"\n" +" Options::root()[\"{}\"].setConditionallyUsed();\n" +"\n" +"將某個區段或值標記為依賴其他值,從而在這項檢查中忽略它。或者,\n" +"如果您確定以上輸入不是錯誤,可以設定\n" +"'input:error_on_unused_options=false' 來關閉這項未使用選項檢查。\n" +"您也隨時可以設定 'input:validate=true',在不執行完整模擬的情況下\n" +"檢查輸入。\n" +"\n" +"{}" -#: ../src/bout++.cxx:382 +#: ../src/bout++.cxx:389 #, c++-format msgid "" " --print-config\t\tPrint the compile-time configuration\n" @@ -399,118 +479,148 @@ msgid "" "For all possible input parameters, see the user manual and/or the physics " "model source (e.g. {:s}.cxx)\n" msgstr "" +" --print-config\t\t顯示編譯時設定\n" +" --list-solvers\t\t列出可用的時間求解器\n" +" --help-solver \t顯示指定時間求解器的說明\n" +" --list-laplacians\t\t列出可用的拉普拉斯反演求解器\n" +" --help-laplacian \t顯示指定拉普拉斯反演求解器的說明\n" +" --list-laplacexz\t\t列出可用的 LaplaceXZ 反演求解器\n" +" --help-laplacexz \t顯示指定 LaplaceXZ 反演求解器的說明\n" +" --list-invertpars\t\t列出可用的 InvertPar 求解器\n" +" --help-invertpar \t顯示指定 InvertPar 求解器的說明\n" +" --list-rkschemes\t\t列出可用的 Runge-Kutta 格式\n" +" --help-rkscheme \t顯示指定 Runge-Kutta 格式的說明\n" +" --list-meshes\t\t\t列出可用的 Mesh\n" +" --help-mesh \t\t顯示指定 Mesh 的說明\n" +" --list-xzinterpolations\t列出可用的 XZInterpolation\n" +" --help-xzinterpolation \t顯示指定 XZInterpolation 的說明\n" +" --list-zinterpolations\t列出可用的 ZInterpolation\n" +" --help-zinterpolation \t顯示指定 ZInterpolation 的說明\n" +" -h, --help\t\t\t此訊息\n" +" restart [append]\t\t重新啟動模擬。若指定 append,則附加到現有輸出檔案,否則覆寫它們\n" +" VAR=VALUE\t\t\t為輸入參數 VAR 指定一個 VALUE\n" +"\n" +"有關所有可能的輸入參數,請參閱使用者手冊和/或物理模型原始碼(例如 {:s}.cxx)\n" -#: ../src/bout++.cxx:379 +#: ../src/bout++.cxx:386 msgid " -c, --color\t\t\tColor output using bout-log-color\n" msgstr "" +" -c, --color\t\t\t使用 bout-log-color 輸出彩色日誌\n" -#: ../include/bout/options.hxx:823 +#: ../include/bout/options.hxx:899 msgid ") overwritten with:" -msgstr "" +msgstr ") 被覆寫為:" -#: ../src/bout++.cxx:550 +#: ../src/bout++.cxx:557 #, c++-format msgid ", level {}" -msgstr "" - -#: ../src/bout++.cxx:579 -#, c++-format -msgid ", using {} threads" -msgstr "" +msgstr ", 等級 {}" #: ../tests/unit/src/test_bout++.cxx:352 msgid "4 of 8" -msgstr "" +msgstr "4 / 8" -#: ../src/sys/options.cxx:868 +#: ../src/sys/options.cxx:895 msgid "All options used\n" msgstr "" +"所有選項皆已使用\n" -#: ../src/bout++.cxx:528 -#, fuzzy, c++-format +#: ../src/bout++.cxx:535 +#, c++-format msgid "BOUT++ version {:s}\n" -msgstr "BOUT++ 版 {:s}\n" +msgstr "" +"BOUT++ 版本 {:s}\n" -#: ../src/bout++.cxx:143 +#: ../src/bout++.cxx:147 msgid "Bad command line arguments:\n" msgstr "" +"錯誤的命令列參數:\n" + +#: ../src/sys/expressionparser.cxx:192 +#, c++-format +msgid "Boolean operator argument {:e} is not a bool" +msgstr "布林運算子參數 {:e} 不是布林值" -#: ../src/mesh/impls/bout/boutmesh.cxx:559 +#: ../src/mesh/impls/bout/boutmesh.cxx:595 msgid "Boundary regions in this processor: " -msgstr "" +msgstr "此處理器中的邊界區域:" -#: ../src/mesh/impls/bout/boutmesh.cxx:355 +#: ../src/mesh/impls/bout/boutmesh.cxx:348 #, c++-format msgid "Cannot split {:d} X points equally between {:d} processors\n" msgstr "" +"無法將 {:d} 個 X 點平均分配給 {:d} 個處理器\n" -#: ../src/bout++.cxx:818 +#: ../src/bout++.cxx:829 msgid "Check if a file exists, and exit if it does." -msgstr "" +msgstr "檢查檔案是否存在,若存在則結束。" -#: ../src/bout++.cxx:533 -#, fuzzy, c++-format +#: ../src/bout++.cxx:540 +#, c++-format msgid "" "Code compiled on {:s} at {:s}\n" "\n" msgstr "" -"代碼於 {:s} {:s} 编译\n" +"程式碼編譯於 {:s},時間 {:s}\n" "\n" #: ../src/sys/optionsreader.cxx:130 msgid "Command line" -msgstr "" +msgstr "命令列" -#: ../src/bout++.cxx:544 ../tests/unit/src/test_bout++.cxx:358 +#: ../src/bout++.cxx:551 ../tests/unit/src/test_bout++.cxx:358 msgid "Compile-time options:\n" -msgstr "編譯選項:\n" +msgstr "" +"編譯選項:\n" #: ../tests/unit/src/test_bout++.cxx:362 -#, fuzzy msgid "Compiled with flags" -msgstr "\t用設置編譯: {:s}\n" +msgstr "編譯旗標" -#: ../src/mesh/impls/bout/boutmesh.cxx:568 +#: ../src/mesh/impls/bout/boutmesh.cxx:604 msgid "Constructing default regions" -msgstr "" +msgstr "正在建立預設區域" -#: ../src/bout++.cxx:520 -#, fuzzy, c++-format +#: ../src/bout++.cxx:527 +#, c++-format msgid "Could not create PID file {:s}" -msgstr "無法打開輸出文件 '{:s}'\n" +msgstr "無法建立 PID 檔案 {:s}" -#: ../src/mesh/impls/bout/boutmesh.cxx:318 +#: ../src/mesh/impls/bout/boutmesh.cxx:309 msgid "" "Could not find a valid value for NXPE. Try a different number of processors." msgstr "無法找到NXPE的有效值。 嘗試不同數量的處理器。" #: ../src/sys/options/options_ini.cxx:160 -#, fuzzy, c++-format +#, c++-format msgid "Could not open output file '{:s}'\n" -msgstr "無法打開輸出文件 '{:s}'\n" +msgstr "" +"無法開啟輸出檔案 '{:s}'\n" -#: ../src/bout++.cxx:652 +#: ../src/bout++.cxx:657 #, c++-format msgid "Could not open {:s}/{:s}.{:d} for writing" -msgstr "" +msgstr "無法開啟 {:s}/{:s}.{:d} 以供寫入" #. Error reading -#: ../src/mesh/mesh.cxx:532 +#: ../src/mesh/mesh.cxx:543 #, c++-format msgid "Could not read integer array '{:s}'\n" msgstr "" +"無法讀取整數陣列 '{:s}'\n" #. Failed . Probably not important enough to stop the simulation -#: ../src/bout++.cxx:632 +#: ../src/bout++.cxx:637 msgid "Could not run bout-log-color. Make sure it is in your PATH\n" msgstr "" +"無法執行 bout-log-color。請確認它在您的 PATH 中\n" -#: ../src/solver/solver.cxx:765 +#: ../src/solver/solver.cxx:772 #, c++-format msgid "Couldn't add Monitor: {:g} is not a multiple of {:g}!" -msgstr "" +msgstr "無法加入監視器:{:g} 不是 {:g} 的整數倍!" -#: ../src/sys/expressionparser.cxx:273 +#: ../src/sys/expressionparser.cxx:312 #, c++-format msgid "" "Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so " @@ -518,195 +628,216 @@ msgid "" "may need to change your input file.\n" "{}" msgstr "" +"找不到產生器 '{}'。BOUT++ 表達式現在區分大小寫,因此\n" +"您可能需要修改輸入檔案。\n" +"{}" -#: ../src/mesh/mesh.cxx:568 +#: ../src/mesh/mesh.cxx:587 #, c++-format msgid "Couldn't find region {:s} in regionMap2D" -msgstr "" +msgstr "無法在 regionMap2D 中找到區域 {:s}" -#: ../src/mesh/mesh.cxx:560 +#: ../src/mesh/mesh.cxx:571 ../src/mesh/mesh.cxx:579 #, c++-format msgid "Couldn't find region {:s} in regionMap3D" -msgstr "" +msgstr "無法在 regionMap3D 中找到區域 {:s}" -#: ../src/mesh/mesh.cxx:576 +#: ../src/mesh/mesh.cxx:595 #, c++-format msgid "Couldn't find region {:s} in regionMapPerp" -msgstr "" +msgstr "無法在 regionMapPerp 中找到區域 {:s}" #. Convert any exceptions to something a bit more useful -#: ../src/sys/options.cxx:336 +#: ../src/sys/options.cxx:361 #, c++-format msgid "Couldn't get {} from option {:s} = '{:s}': {}" -msgstr "" +msgstr "無法從選項 {1:s} = '{2:s}' 取得 {0}:{3}" -#: ../src/bout++.cxx:508 -#, fuzzy, c++-format +#: ../src/bout++.cxx:515 +#, c++-format msgid "DataDir \"{:s}\" does not exist or is not accessible\n" -msgstr "\"{:s}\" 不存在或不可訪問\n" +msgstr "" +"資料目錄 \"{:s}\" 不存在或無法存取\n" -#: ../src/bout++.cxx:505 -#, fuzzy, c++-format +#: ../src/bout++.cxx:512 +#, c++-format msgid "DataDir \"{:s}\" is not a directory\n" -msgstr "\"{:s}\" 不是目錄\n" +msgstr "" +"資料目錄 \"{:s}\" 不是目錄\n" -#: ../src/solver/solver.cxx:665 +#: ../src/solver/solver.cxx:671 msgid "ERROR: Solver is already initialised\n" msgstr "" +"錯誤:求解器已初始化\n" -#: ../src/bout++.cxx:209 -#, fuzzy, c++-format +#: ../src/bout++.cxx:216 +#, c++-format msgid "Error encountered during initialisation: {:s}\n" -msgstr "啟動時遇到錯誤 : {:s}\n" +msgstr "" +"初始化期間發生錯誤:{:s}\n" -#: ../src/bout++.cxx:744 +#: ../src/bout++.cxx:751 msgid "Error whilst writing settings" -msgstr "" +msgstr "寫入設定時發生錯誤" -#: ../src/mesh/impls/bout/boutmesh.cxx:332 +#: ../src/mesh/impls/bout/boutmesh.cxx:323 #, c++-format msgid "Error: nx must be greater than 2 times MXG (2 * {:d})" -msgstr "" +msgstr "錯誤:nx 必須大於 2 倍 MXG(2 * {:d})" -#: ../src/solver/solver.cxx:512 +#: ../src/solver/solver.cxx:520 msgid "Failed to initialise solver-> Aborting\n" msgstr "" +"初始化求解器失敗 -> 中止\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:290 +#: ../src/mesh/impls/bout/boutmesh.cxx:281 #, c++-format msgid "Finding value for NXPE (ideal = {:f})\n" msgstr "" +"正在尋找 NXPE 的值(理想值 = {:f})\n" -#: ../src/solver/solver.cxx:668 +#: ../src/solver/solver.cxx:674 msgid "Initialising solver\n" -msgstr "初始化求解器\n" +msgstr "" +"初始化求解器\n" -#: ../src/bout++.cxx:494 +#: ../src/bout++.cxx:501 msgid "" "Input and output file for settings must be different.\n" "Provide -o to avoid this issue.\n" msgstr "" +"設定的輸入檔與輸出檔必須不同。\n" +"請提供 -o 以避免此問題。\n" #: ../src/sys/optionsreader.cxx:76 msgid "Invalid command line option '-' found - maybe check whitespace?" -msgstr "" +msgstr "找到無效的命令列選項 '-',也許應檢查空白字元?" -#: ../src/mesh/impls/bout/boutmesh.cxx:400 +#: ../src/mesh/impls/bout/boutmesh.cxx:436 msgid "Loading mesh" msgstr "加載網格" -#: ../src/mesh/impls/bout/boutmesh.cxx:415 +#: ../src/mesh/impls/bout/boutmesh.cxx:451 msgid "Mesh must contain nx" -msgstr "" +msgstr "網格必須包含 nx" -#: ../src/mesh/impls/bout/boutmesh.cxx:419 +#: ../src/mesh/impls/bout/boutmesh.cxx:455 msgid "Mesh must contain ny" -msgstr "" +msgstr "網格必須包含 ny" #. Not found -#: ../src/mesh/mesh.cxx:536 +#: ../src/mesh/mesh.cxx:547 #, c++-format msgid "Missing integer array {:s}\n" msgstr "" +"缺少整數陣列 {:s}\n" -#: ../src/solver/solver.cxx:905 +#: ../src/solver/solver.cxx:911 #, c++-format msgid "Monitor signalled to quit (exception {})\n" msgstr "" +"監視器發出離開訊號(例外 {})\n" -#: ../src/solver/solver.cxx:883 +#: ../src/solver/solver.cxx:889 #, c++-format msgid "Monitor signalled to quit (return code {})" -msgstr "" +msgstr "監視器發出離開訊號(回傳碼 {})" -#: ../src/bout++.cxx:823 +#: ../src/bout++.cxx:834 msgid "Name of file whose existence triggers a stop" -msgstr "" +msgstr "其存在會觸發停止的檔案名稱" -#: ../src/mesh/impls/bout/boutmesh.cxx:565 +#: ../src/mesh/impls/bout/boutmesh.cxx:601 msgid "No boundary regions in this processor" -msgstr "" +msgstr "此處理器中沒有邊界區域" -#: ../src/mesh/impls/bout/boutmesh.cxx:550 +#: ../src/mesh/impls/bout/boutmesh.cxx:586 msgid "No boundary regions; domain is periodic\n" msgstr "" +"沒有邊界區域;區域是週期性的\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:254 +#: ../src/mesh/impls/bout/boutmesh.cxx:245 #, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n" msgstr "" +"處理器數量 ({:d}) 無法被 x 方向上的 NP 數 ({:d}) 整除\n" -#: ../src/mesh/impls/bout/boutmesh.cxx:267 +#: ../src/mesh/impls/bout/boutmesh.cxx:258 #, c++-format msgid "" "Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n" msgstr "" +"處理器數量 ({:d}) 無法被 y 方向上的 NP 數 ({:d}) 整除\n" #. Less than 2 time-steps left -#: ../src/bout++.cxx:896 +#: ../src/bout++.cxx:908 #, c++-format msgid "Only {:e} seconds ({:.2f} steps) left. Quitting\n" msgstr "" +"只剩下 {:e} 秒({:.2f} 步)。正在結束\n" -#: ../src/sys/options.cxx:303 ../src/sys/options.cxx:345 -#: ../src/sys/options.cxx:393 ../src/sys/options.cxx:428 -#: ../src/sys/options.cxx:703 ../src/sys/options.cxx:730 -#: ../src/sys/options.cxx:757 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:382 ../src/sys/options.cxx:398 +#: ../src/sys/options.cxx:441 ../src/sys/options.cxx:471 +#: ../src/sys/options.cxx:745 ../src/sys/options.cxx:767 +#: ../src/sys/options.cxx:789 +#, c++-format msgid "Option {:s} has no value" -msgstr "\"{:s}\" 不是目錄\n" +msgstr "選項 {:s} 沒有值" #. Doesn't exist -#: ../src/sys/options.cxx:159 -#, fuzzy, c++-format +#: ../src/sys/options.cxx:172 +#, c++-format msgid "Option {:s}:{:s} does not exist" -msgstr "選項{:s}:{:s}不存在" +msgstr "選項 {:s}:{:s} 不存在" -#: ../include/bout/options.hxx:828 +#: ../include/bout/options.hxx:904 #, c++-format msgid "" "Options: Setting a value from same source ({:s}) to new value '{:s}' - old " "value was '{:s}'." -msgstr "" +msgstr "選項:正在將來自相同來源 ({:s}) 的值設定為新值 '{:s}',舊值為 '{:s}'。" -#: ../src/mesh/impls/bout/boutmesh.cxx:552 +#: ../src/mesh/impls/bout/boutmesh.cxx:588 msgid "Possible boundary regions are: " -msgstr "" +msgstr "可能的邊界區域有:" -#: ../src/bout++.cxx:538 +#: ../src/bout++.cxx:545 #, c++-format msgid "" "Processor number: {:d} of {:d}\n" "\n" msgstr "" +"處理器編號:{:d} / {:d}\n" +"\n" -#: ../src/mesh/mesh.cxx:609 +#: ../src/mesh/mesh.cxx:642 #, c++-format msgid "Registered region 2D {:s}" -msgstr "" +msgstr "已註冊 2D 區域 {:s}" -#: ../src/mesh/mesh.cxx:599 +#: ../src/mesh/mesh.cxx:632 #, c++-format msgid "Registered region 3D {:s}" -msgstr "" +msgstr "已註冊 3D 區域 {:s}" -#: ../src/mesh/mesh.cxx:619 +#: ../src/mesh/mesh.cxx:652 #, c++-format msgid "Registered region Perp {:s}" -msgstr "" +msgstr "已註冊 Perp 區域 {:s}" -#: ../src/bout++.cxx:529 -#, fuzzy, c++-format +#: ../src/bout++.cxx:536 +#, c++-format msgid "Revision: {:s}\n" -msgstr "版: {:s}\n" +msgstr "" +"修訂版:{:s}\n" -#: ../src/solver/solver.cxx:581 +#: ../src/solver/solver.cxx:587 msgid "Run time : " msgstr "計算時間" #. / Run the solver -#: ../src/solver/solver.cxx:525 +#: ../src/solver/solver.cxx:533 msgid "" "Running simulation\n" "\n" @@ -716,72 +847,73 @@ msgstr "" #: ../tests/unit/src/test_bout++.cxx:359 msgid "Signal" -msgstr "" +msgstr "訊號" -#: ../src/bout++.cxx:865 +#: ../src/bout++.cxx:876 msgid "" "Sim Time | RHS evals | Wall Time | Calc Inv Comm I/O SOLVER\n" "\n" msgstr "" -"模擬時間 | 評估數量 | 時鐘時間 | 計算 逆溫 通訊 輸入輸出 時" -"間整合\n" +"模擬時間 | 評估數量 | 時鐘時間 | 計算 逆溫 通訊 輸入輸出 時間整合\n" "\n" -#: ../src/bout++.cxx:868 +#: ../src/bout++.cxx:879 msgid "" "Sim Time | RHS_e evals | RHS_I evals | Wall Time | Calc Inv " "Comm I/O SOLVER\n" "\n" msgstr "" -"模擬時間 | 評估數量(e) | 評估數量(I) | 時鐘時間 | 計算 逆溫 " -"通訊 輸入輸出 時間整合\n" +"模擬時間 | 評估數量(e) | 評估數量(I) | 時鐘時間 | 計算 逆溫 通訊 輸入輸出 時間整合\n" "\n" -#: ../src/solver/solver.cxx:506 +#: ../src/solver/solver.cxx:514 #, c++-format msgid "Solver running for {:d} outputs with monitor timestep of {:e}\n" msgstr "" +"求解器正在執行 {:d} 個輸出,監視步長為 {:e}\n" -#: ../src/solver/solver.cxx:502 +#: ../src/solver/solver.cxx:510 #, c++-format msgid "Solver running for {:d} outputs with output timestep of {:e}\n" msgstr "" +"求解器正在執行 {:d} 個輸出,輸出步長為 {:e}\n" -#: ../src/solver/solver.cxx:781 +#: ../src/solver/solver.cxx:788 #, c++-format msgid "" "Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) after init is " "called!" -msgstr "" +msgstr "Solver::addMonitor:在呼叫 init 之後無法將時間步長從 {:g} 降低到 {:g}!" -#: ../src/solver/solver.cxx:1281 +#: ../src/solver/solver.cxx:1289 #, c++-format msgid "" "Time derivative at wrong location - Field is at {:s}, derivative is at {:s} " "for field '{:s}'\n" msgstr "" +"時間導數位於錯誤位置 - 欄位位於 {:s},導數位於 {:s},對應欄位 '{:s}'\n" -#: ../src/solver/solver.cxx:1480 +#: ../src/solver/solver.cxx:1494 #, c++-format msgid "Time derivative for variable '{:s}' not set" -msgstr "" +msgstr "變數 '{:s}' 的時間導數尚未設定" -#: ../src/mesh/mesh.cxx:605 +#: ../src/mesh/mesh.cxx:638 #, c++-format msgid "Trying to add an already existing region {:s} to regionMap2D" -msgstr "" +msgstr "嘗試將已存在的區域 {:s} 加入 regionMap2D" -#: ../src/mesh/mesh.cxx:595 +#: ../src/mesh/mesh.cxx:614 #, c++-format msgid "Trying to add an already existing region {:s} to regionMap3D" -msgstr "" +msgstr "嘗試將已存在的區域 {:s} 加入 regionMap3D" -#: ../src/mesh/mesh.cxx:616 +#: ../src/mesh/mesh.cxx:649 #, c++-format msgid "Trying to add an already existing region {:s} to regionMapPerp" -msgstr "" +msgstr "嘗試將已存在的區域 {:s} 加入 regionMapPerp" -#: ../src/sys/options.cxx:99 ../src/sys/options.cxx:138 +#: ../src/sys/options.cxx:112 ../src/sys/options.cxx:151 #, c++-format msgid "" "Trying to index Option '{0}' with '{1}', but '{0}' is a value, not a " @@ -789,157 +921,188 @@ msgid "" "This is likely the result of clashing input options, and you may have to " "rename one of them.\n" msgstr "" +"正在嘗試以 '{1}' 索引選項 '{0}',但 '{0}' 是一個值,而不是一個區段。\n" +"這很可能是輸入選項互相衝突的結果,您可能需要重新命名其中之一。\n" -#: ../src/mesh/coordinates.cxx:1462 +#: ../src/mesh/coordinates.cxx:1464 msgid "" "Unrecognised paralleltransform option.\n" "Valid choices are 'identity', 'shifted', 'fci'" msgstr "" +"無法辨識的 paralleltransform 選項。\n" +"有效選項為 'identity'、'shifted'、'fci'" -#: ../src/sys/options.cxx:872 +#: ../src/sys/options.cxx:899 msgid "Unused options:\n" msgstr "" +"未使用的選項:\n" -#: ../src/bout++.cxx:439 +#: ../src/bout++.cxx:446 #, c++-format msgid "Usage is {:s} -d \n" msgstr "" +"用法:{:s} -d <資料目錄>\n" -#: ../src/bout++.cxx:448 +#: ../src/bout++.cxx:455 #, c++-format msgid "Usage is {:s} -f \n" msgstr "" +"用法:{:s} -f <選項檔名>\n" -#: ../src/bout++.cxx:466 +#: ../src/bout++.cxx:473 #, c++-format msgid "Usage is {:s} -l \n" msgstr "" +"用法:{:s} -l <日誌檔名>\n" -#: ../src/bout++.cxx:457 +#: ../src/bout++.cxx:464 #, c++-format msgid "Usage is {:s} -o \n" msgstr "" +"用法:{:s} -o <設定檔名>\n" -#: ../src/bout++.cxx:353 +#: ../src/bout++.cxx:360 #, c++-format msgid "Usage is {} {} \n" msgstr "" +"用法:{} {} <名稱>\n" #: ../tests/unit/src/test_bout++.cxx:32 ../tests/unit/src/test_bout++.cxx:46 msgid "Usage:" -msgstr "" +msgstr "用法:" #. Print help message -- note this will be displayed once per processor as we've not #. started MPI yet. -#: ../src/bout++.cxx:367 +#: ../src/bout++.cxx:374 #, c++-format msgid "" "Usage: {:s} [-d ] [-f ] [restart [append]] " "[VAR=VALUE]\n" msgstr "" +"用法:{:s} [-d <資料目錄>] [-f <選項檔名>] [restart [append]] [VAR=VALUE]\n" #. restart file should be written by physics model -#: ../src/solver/solver.cxx:921 +#: ../src/solver/solver.cxx:927 msgid "User signalled to quit. Returning\n" msgstr "" +"使用者發出離開訊號。正在返回\n" + +#: ../src/sys/options.cxx:486 +#, c++-format +msgid "Value for option {:s} = {:e} is not a bool" +msgstr "選項 {:s} = {:e} 的值不是布林值" -#: ../src/sys/options.cxx:373 +#: ../src/sys/options.cxx:426 #, c++-format msgid "Value for option {:s} = {:e} is not an integer" -msgstr "" +msgstr "選項 {:s} = {:e} 的值不是整數" -#: ../src/sys/options.cxx:408 +#: ../src/sys/options.cxx:456 #, c++-format msgid "Value for option {:s} cannot be converted to a BoutReal" -msgstr "" +msgstr "選項 {:s} 的值無法轉換為 BoutReal" -#: ../src/sys/options.cxx:581 +#: ../src/sys/options.cxx:623 #, c++-format msgid "Value for option {:s} cannot be converted to a Field2D" -msgstr "" +msgstr "選項 {:s} 的值無法轉換為 Field2D" -#: ../src/sys/options.cxx:529 +#: ../src/sys/options.cxx:571 #, c++-format msgid "Value for option {:s} cannot be converted to a Field3D" -msgstr "" +msgstr "選項 {:s} 的值無法轉換為 Field3D" -#: ../src/sys/options.cxx:663 +#: ../src/sys/options.cxx:705 #, c++-format msgid "Value for option {:s} cannot be converted to a FieldPerp" -msgstr "" +msgstr "選項 {:s} 的值無法轉換為 FieldPerp" -#: ../src/sys/options.cxx:451 +#: ../src/sys/options.cxx:491 #, c++-format msgid "Value for option {:s} cannot be converted to a bool" -msgstr "" +msgstr "選項 {:s} 的值無法轉換為 bool" -#: ../src/sys/options.cxx:709 +#: ../src/sys/options.cxx:751 #, c++-format msgid "Value for option {:s} cannot be converted to an Array" -msgstr "" +msgstr "選項 {:s} 的值無法轉換為 Array" -#: ../src/sys/options.cxx:736 +#: ../src/sys/options.cxx:773 #, c++-format msgid "Value for option {:s} cannot be converted to an Matrix" -msgstr "" +msgstr "選項 {:s} 的值無法轉換為 Matrix" -#: ../src/sys/options.cxx:763 +#: ../src/sys/options.cxx:795 #, c++-format msgid "Value for option {:s} cannot be converted to an Tensor" -msgstr "" +msgstr "選項 {:s} 的值無法轉換為 Tensor" #. Another type which can't be converted -#: ../src/sys/options.cxx:365 +#: ../src/sys/options.cxx:418 #, c++-format msgid "Value for option {:s} is not an integer" -msgstr "" +msgstr "選項 {:s} 的值不是整數" -#: ../src/solver/solver.cxx:1232 ../src/solver/solver.cxx:1238 +#: ../src/solver/solver.cxx:1240 ../src/solver/solver.cxx:1246 #, c++-format msgid "Variable '{:s}' not initialised" -msgstr "" +msgstr "變數 '{:s}' 尚未初始化" -#: ../src/mesh/impls/bout/boutmesh.cxx:431 +#: ../src/mesh/impls/bout/boutmesh.cxx:467 #, c++-format msgid "" "WARNING: Number of toroidal points should be 2^n for efficient FFT " "performance -- consider changing MZ ({:d}) if using FFTs\n" msgstr "" +"警告:為了獲得高效的 FFT 效能,環向點數應為 2^n;若使用 FFT,請考慮修改 MZ ({:d})\n" -#: ../src/mesh/coordinates.cxx:633 +#: ../src/mesh/coordinates.cxx:635 msgid "WARNING: extrapolating input mesh quantities into x-boundary cells\n" msgstr "" +"警告:正在將輸入網格量外推到 x 邊界單元\n" -#: ../src/mesh/coordinates.cxx:410 +#: ../src/mesh/coordinates.cxx:412 msgid "" "WARNING: extrapolating input mesh quantities into x-boundary cells. Set " "option extrapolate_x=false to disable this.\n" msgstr "" +"警告:正在將輸入網格量外推到 x 邊界單元。設定選項 extrapolate_x=false 可停用此行為。\n" -#: ../src/mesh/coordinates.cxx:638 +#: ../src/mesh/coordinates.cxx:640 msgid "WARNING: extrapolating input mesh quantities into y-boundary cells\n" msgstr "" +"警告:正在將輸入網格量外推到 y 邊界單元\n" -#: ../src/mesh/coordinates.cxx:415 +#: ../src/mesh/coordinates.cxx:417 msgid "" "WARNING: extrapolating input mesh quantities into y-boundary cells. Set " "option extrapolate_y=false to disable this.\n" msgstr "" +"警告:正在將輸入網格量外推到 y 邊界單元。設定選項 extrapolate_y=false 可停用此行為。\n" -#: ../src/bout++.cxx:814 +#: ../src/bout++.cxx:825 msgid "Wall time limit in hours. By default (< 0), no limit" -msgstr "" +msgstr "牆鐘時間限制(小時)。預設值(< 0)表示不限制" #: ../src/sys/optionsreader.cxx:42 -#, fuzzy, c++-format +#, c++-format msgid "Writing options to file {:s}\n" -msgstr "寫選項到文件 " +msgstr "" +"正在將選項寫入檔案 {:s}\n" #. / The source label given to default values -#: ../src/sys/options.cxx:15 +#: ../src/sys/options.cxx:34 msgid "default" msgstr "默认设置" +#, fuzzy, c++-format +#~ msgid "\tOpenMP parallelisation {}" +#~ msgstr "\tOpenMP並行化已禁用\n" + +#, fuzzy, c++-format +#~ msgid "\tOption '{:s}': Boolean expected. Got '{:s}'\n" +#~ msgstr "\t選項 '{:s}': 布爾預期. 拿到 '{:s}'\n" + #~ msgid "\tChecking disabled\n" #~ msgstr "\t測試關掉\n" @@ -947,13 +1110,5 @@ msgstr "默认设置" #~ msgid "\tChecking enabled, level {:d}\n" #~ msgstr "\t測試打開,级别 {:d}\n" -#, fuzzy -#~ msgid "\tOpenMP parallelisation enabled, using {:d} threads\n" -#~ msgstr "\t啟用OpenMP並行化。 使用{:d}個線程\n" - -#, fuzzy -#~ msgid "Option {:s} is not a section" -#~ msgstr "\"{:s}\" 不是目錄\n" - #~ msgid "Error encountered during initialisation\n" #~ msgstr "啟動時遇到錯誤\n" diff --git a/manual/CMakeLists.txt b/manual/CMakeLists.txt index c8e22b1dcf..87f5101e58 100644 --- a/manual/CMakeLists.txt +++ b/manual/CMakeLists.txt @@ -6,28 +6,37 @@ find_package(Sphinx REQUIRED) set(BOUT_SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/sphinx) set(BOUT_SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs) -add_custom_target(sphinx-html - COMMAND ${SPHINX_EXECUTABLE} -b html ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} - COMMAND ${CMAKE_COMMAND} -E echo "Generated HTML docs in file://${BOUT_SPHINX_BUILD}/index.html" +set(env_command ${CMAKE_COMMAND} -E env + PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} +) + +add_custom_target( + sphinx-html + COMMAND ${env_command} ${SPHINX_EXECUTABLE} -b html ${BOUT_SPHINX_SOURCE} + ${BOUT_SPHINX_BUILD} + COMMAND ${CMAKE_COMMAND} -E echo + "Generated HTML docs in file://${BOUT_SPHINX_BUILD}/index.html" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating HTML documentation with Sphinx in ${BOUT_SPHINX_BUILD}" ) -add_custom_target(sphinx-pdf - COMMAND ${SPHINX_EXECUTABLE} -M latexpdf ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} - COMMAND ${CMAKE_COMMAND} -E echo "Generated PDF docs in file://${BOUT_SPHINX_BUILD}" +add_custom_target( + sphinx-pdf + COMMAND ${env_command} ${SPHINX_EXECUTABLE} -M latexpdf ${BOUT_SPHINX_SOURCE} + ${BOUT_SPHINX_BUILD} + COMMAND ${CMAKE_COMMAND} -E echo + "Generated PDF docs in file://${BOUT_SPHINX_BUILD}" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating PDF documentation with Sphinx in ${BOUT_SPHINX_BUILD}" ) -set_target_properties(sphinx-html sphinx-pdf PROPERTIES - ENVIRONMENT PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} -) - -add_custom_target(docs ALL) +add_custom_target(docs) add_dependencies(docs sphinx-html) -install(DIRECTORY ${BOUT_SPHINX_BUILD}/ +install( + DIRECTORY ${BOUT_SPHINX_BUILD}/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/bout++/ + EXCLUDE_FROM_ALL + COMPONENT docs PATTERN .* EXCLUDE ) diff --git a/manual/RELEASE_HOWTO.md b/manual/RELEASE_HOWTO.md index 4103e6f197..d0a6ddb231 100644 --- a/manual/RELEASE_HOWTO.md +++ b/manual/RELEASE_HOWTO.md @@ -29,11 +29,10 @@ Before merging PR: - Be aware that this *will* update the timestamps and *possibly* reorder file paths in the .po and .pot files - [ ] Update [`CHANGELOG.md`][changelog]: - - Run [bout-changelog-generator.py LAST_RELEASE NEXT_RELEASE][bin/bout-changelog-generator.py] + - Run [`bout-changelog-generator.py LAST_RELEASE NEXT_RELEASE`][bin/bout-changelog-generator.py] - See the docs for how to get the token -- [ ] Get list of authors: - - [ ] `git log --format='%aN' | sort | uniq` - - [ ] Compare to list in [`CITATION.cff`][citation], add new authors +- [ ] Run [`update_citations.py`][bin/update_citations.py] to add new + authors to [`CITATION.cff`](CITATION.cff) - [ ] Prep a new Zenodo release: - https://doi.org/10.5281/zenodo.1423212 - "New Version" @@ -44,13 +43,8 @@ Before merging PR: - [ ] Change DOI in [`README.md`][README] to new DOI - [ ] Change date-released in [`CITATION.cff`][citation] - [ ] Check `abidiff` to see if `soname` needs bumping in `makefile`: -- [ ] Change version number, removing prerelease tag in: - - [ ] [`configure.ac`][configure]: `AC_INIT` - - [ ] [`CITATION.cff`][citation]: `version` - - [ ] [`manual/sphinx/conf.py`][sphinx_conf]: `version` and `release` - - [ ] [`manual/doxygen/Doxyfile_readthedocs`][Doxyfile_readthedocs]: `PROJECT_NUMBER` - - [ ] [`manual/doxygen/Doxyfile`][Doxyfile]: `PROJECT_NUMBER` - - [ ] [`CMakeLists.txt`][CMakeLists]: `_bout_previous_version`, `_bout_next_version` +- [ ] Run [`update_version_number.py LAST_RELEASE NEXT_RELEASE`][bin/update_version_number.py] +- [ ] Update what version of PETSc and SUNDIALS we support (upper bound) After PR is merged: diff --git a/manual/doxygen/Doxyfile b/manual/doxygen/Doxyfile index 03a0588078..b7ae4d7a58 100644 --- a/manual/doxygen/Doxyfile +++ b/manual/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = BOUT++ # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 5.1.1 +PROJECT_NUMBER = 5.2.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -787,7 +787,8 @@ EXCLUDE = ../../examples \ ../../manual/ \ ../../bin/ \ ../../externalpackages/googletest/ \ - ../../tests/ + ../../tests/ \ + ../../build/_deps/ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -1016,13 +1017,11 @@ PREDEFINED = BACKTRACE \ BOUT_HAS_LAPACK \ BOUT_HAS_NETCDF \ BOUT_HAS_PETSC \ - BOUT_HAS_PRETTY_FUNCTION \ BOUT_HAS_PVODE \ BOUT_HAS_SCOREP \ BOUT_HAS_SLEPC \ BOUT_HAS_SUNDIALS \ BOUT_HAS_UUID_SYSTEM_GENERATOR \ - BOUT_USE_BACKTRACE \ BOUT_USE_COLOR \ BOUT_USE_OPENMP \ BOUT_USE_OUTPUT_DEBUG \ diff --git a/manual/sphinx/conf.py b/manual/sphinx/conf.py index 6df57c2dd2..cedf37c5ee 100755 --- a/manual/sphinx/conf.py +++ b/manual/sphinx/conf.py @@ -72,35 +72,29 @@ def __getattr__(cls, name): sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) print(os.environ) print(sys.argv) - python = sys.argv[0] - pydir = "/".join(python.split("/")[:-2]) os.system("which clang-format") os.system("which clang-format-6.0") - os.system( - "git submodule update --init --recursive ../../externalpackages/mpark.variant" - ) - pwd = "/".join(os.getcwd().split("/")[:-2]) - os.system("git submodule update --init --recursive ../../externalpackages/fmt") + subprocess.run("git submodule update --init --recursive", shell=True) cmake = ( - "cmake . -DBOUT_USE_FFTW=ON" - + " -DBOUT_USE_LAPACK=OFF" - + " -DBOUT_ENABLE_PYTHON=ON" - + " -DBOUT_UPDATE_GIT_SUBMODULE=OFF" - + " -DBOUT_TESTS=OFF" - + " -DBOUT_ALLOW_INSOURCE_BUILD=ON" - + f" -DPython3_ROOT_DIR={pydir}" - + f" -Dmpark_variant_DIR={pwd}/externalpackages/mpark.variant/" - + f" -Dfmt_DIR={pwd}/externalpackages/fmt/" + "cmake -S ../.. " + " -B bout_build" + " -DBOUT_USE_FFTW=ON" + " -DBOUT_USE_LAPACK=OFF" + " -DBOUT_ENABLE_PYTHON=ON" + " -DBOUT_UPDATE_GIT_SUBMODULE=OFF" + " -DBOUT_TESTS=OFF" + " -DBOUT_ALLOW_INSOURCE_BUILD=ON" + f" -DPython3_ROOT_DIR={sys.exec_prefix}" ) - # os.system("mkdir ../../build") - os.system("echo " + cmake) - x = os.system("cd ../.. ;" + cmake) - assert x == 0 - x = os.system("cd ../.. ; make -j 2 -f Makefile") - assert x == 0 + subprocess.run(f"echo {cmake}", shell=True) + subprocess.run(f"{cmake}", shell=True, check=True) + subprocess.run("cmake --build bout_build", shell=True, check=True) + + # Add the build directory to sys.path so that sphinx picks up the built + # Python modules + sys.path.append("bout_build/tools/pylib") -# readthedocs currently runs out of memory if we actually dare to try to do this if has_breathe: # Run doxygen to generate the XML sources subprocess.run(["doxygen", "Doxyfile"], cwd="../doxygen", check=True) @@ -183,9 +177,9 @@ def __getattr__(cls, name): # built documents. # # The short X.Y version. -version = "5.1" +version = "5.2" # The full version, including alpha/beta/rc tags. -release = "5.1.1" +release = "5.2.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -197,7 +191,7 @@ def __getattr__(cls, name): # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".venv", "venv"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" diff --git a/manual/sphinx/developer_docs/code_layout.rst b/manual/sphinx/developer_docs/code_layout.rst index 833b763ce9..3c02602b44 100644 --- a/manual/sphinx/developer_docs/code_layout.rst +++ b/manual/sphinx/developer_docs/code_layout.rst @@ -131,4 +131,3 @@ The layout of the ``src/`` directory is as follows: - General purpose utilities used throughout the library, such as `BoutException`, wrappers for C libraries like ``PETSc`` and ``HYPRE``, screen and file input and output. - diff --git a/manual/sphinx/developer_docs/contributing.rst b/manual/sphinx/developer_docs/contributing.rst index 4453f518b6..062415af04 100644 --- a/manual/sphinx/developer_docs/contributing.rst +++ b/manual/sphinx/developer_docs/contributing.rst @@ -173,6 +173,86 @@ give you comments to improve the code. You can make additional changes and push them to the same feature branch and they will be automatically added to the pull request. +Running Tests +~~~~~~~~~~~~~ + +We run many tests and checks automatically on GitHub (collectively called "CI") +for a variety of build configurations. See :ref:`sec-runtestsuite` for how to +run them locally. Running the full test suite can take some time, but it's a +good idea to run at least the unit tests locally when you're making changes: + +.. code-block:: console + + cmake --build build --target check-unit-tests + +Along with the automated tests, we also run things like formatters and static +analysis in CI, which may provide further feedback (see :ref:`sec-coding-style` +for more details). If your PR fails the format check on CI, please install the +formatters and run them according to the next section. Usually this is just: + +.. code-block:: console + + git clang-format next + +which will format just the parts of C++ files that have changed since ``next``. + +Formatting and Linters +~~~~~~~~~~~~~~~~~~~~~~ + +For frequent developers, we strongly recommend installing the formatting and +linting tools locally and building them into your regular workflow. You can +install the majority of our developer tools at once using `uv +`_ from the top of your BOUT++ directory: + +.. code-block:: console + + uv sync --only-dev --inexact + +This will install all the developer tools into a virtual environment (creating +one if necessary, typically under ``.venv``). You can then activate the virtual +environment and use the tools manually, or via integrations in your editor. + +We *strongly* recommend using ``uv`` to install the developer tools, as this +will ensure that you get the *exact* versions used in CI and by other +developers, but you could also use ``pip install --group dev`` if you wish. + +The quickest way to run all the formatters on a PR at once is typically: + +.. code-block:: console + + uv tool run prek run --from-ref next + +This will run all our formatters on files that have changed since ``next``. + +The tools can be updated with: + +.. code-block:: console + + uv lock -U + uv tool run sync-with-uv + + +Install Pre-Commit Hooks (Optional but recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We also have a `prek `_ config that can run some of +these automatically using pre-commit hooks -- that is, when you run ``git +commit``, these tools will run and abort the commit if they change any +files. You then just have to stage the new changes and try again. + +Install our developer tools with ``uv`` as above, and then install the +pre-commit hook: + +.. code-block:: console + + prek install + +That's it! ``prek`` will install the tools into their own separate virtual +environment so they won't interfere with any you may have, and ``git`` will take +care of running them automatically. + +.. _sec-coding-style: + Coding Style ------------ @@ -189,6 +269,25 @@ These guidelines are intended to make code easier to read and therefore easier to understand. Being consistent in coding style also helps comprehension by reducing cognitive load. +We use various tools to enforce code style and quality: + +- `clang-format `_ for formatting + of C++ code +- `clang-tidy `_ for linting + and static analysis of C++ code +- `ruff `_ for formatting and linting of Python code +- `cmake-format `_ for formatting + CMake files +- `sphinx-lint `_ for linting + documentation pages + +You can install all of these tools using: + +.. code-block:: console + + pip install -r requirements_dev.txt + + Comments ~~~~~~~~ diff --git a/manual/sphinx/developer_docs/data_types.rst b/manual/sphinx/developer_docs/data_types.rst index fa8e9e6ea6..7feb3945aa 100644 --- a/manual/sphinx/developer_docs/data_types.rst +++ b/manual/sphinx/developer_docs/data_types.rst @@ -255,9 +255,9 @@ parallelise and vectorise. Some tuning of this is possible, see below for details. It replaces the C-style triple-nested loop:: Field3D f(0.0); - for (int i = mesh->xstart; i < mesh->xend; ++i) { - for (int j = mesh->ystart; j < mesh->yend; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int i = mesh->xstart; i <= mesh->xend; ++i) { + for (int j = mesh->ystart; j <= mesh->yend; ++j) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { f(i,j,k) = a(i,j,k) + b(i,j,k) } } @@ -280,7 +280,7 @@ The region to iterate over can be over ``Field2D``, ``Field3D``, or - `RGN_NOY`, which skips the y boundaries and guard cells New regions can be created and modified, see section below. - + A standard C++ range for loop can also be used, but this is unlikely to OpenMP parallelise or vectorise:: @@ -306,7 +306,7 @@ For loops inside parallel regions, there is ``BOUT_FOR_INNER``:: } ... } - + If a more general OpenMP directive is needed, there is ``BOUT_FOR_OMP``:: @@ -314,7 +314,7 @@ If a more general OpenMP directive is needed, there is BOUT_FOR_OMP(i, region, parallel for reduction(max:result)) { result = f[i] > result ? f[i] : result; } - + The iterator provides access to the x, y, z indices:: Field3D f(0.0); @@ -385,14 +385,14 @@ good performance on typical x86_64 hardware. Some simple diagnostics are printed at the start of the BOUT++ output which may help. For example the ``blob2d`` example prints:: - Registered region 3D RGN_ALL: - Total blocks : 1040, min(count)/max(count) : 64 (1040)/ 64 (1040), Max imbalance : 1, Small block count : 0 + Registered region 3D RGN_ALL: + Total blocks : 1040, min(count)/max(count) : 64 (1040)/ 64 (1040), Max imbalance : 1, Small block count : 0 In this case all blocks are the same size, so the ``Max imbalance`` (ratio of maximum to minimum block size) is 1. The ``Small block count`` is currently defined as the number of blocks with a size less than half the maximum block size. Ideally all blocks should be a -similar size, so that work is evenly balanced between threads. +similar size, so that work is evenly balanced between threads. Creating new regions ~~~~~~~~~~~~~~~~~~~~ @@ -422,7 +422,7 @@ in the mask (i.e. set subtraction):: or:: auto region = mask(mesh->getRegion2D("RGN_ALL"), mesh->getRegion2D("RGN_GUARDS")); - + The above example would produce a region containing all the indices in ``RGN_ALL`` which are not in ``RGN_GUARDS``. @@ -444,7 +444,7 @@ In the current implementation overwriting a region, by attempting to add a region which already exists, is not allowed, and will result in a ``BoutException`` being thrown. This restriction may be removed in future. - + .. _sec-rangeiterator: Iterating over ranges @@ -493,33 +493,73 @@ initialised in the constructor. .. _sec-fieldops: -Field2D/Field3D Arithmetic Operators ------------------------------------- - -The arithmetic operators (``+``, ``-``, ``/``, ``*``) for `Field2D` -and `Field3D` are generated automatically using the `Jinja`_ -templating system. This requires Python 3 (2.7 may work, but only 3 is -supported). - -Because this is fairly low-level code, and we don't expect it to -change very much, the generated code is kept in the git -repository. This has the benefit that Python and Jinja are not needed -to build BOUT++, only to change the ``Field`` operator code. - -.. warning:: You should not modify the generated code - directly. Instead, modify the template and re-generate - the code. If you commit changes to the template and/or - driver, make sure to re-generate the code and commit it - as well - -The Jinja template is in ``src/field/gen_fieldops.jinja``, and the -driver is ``src/field/gen_fieldops.py``. The driver loops over every -combination of `BoutReal`, `Field2D`, `Field3D` (collectively just -"fields" here) with the arithmetic operators, and uses the template to -generate the appropriate code. There is some logic in the template to -handle certain combinations of the input fields: for example, for the -binary infix operators, only check the two arguments are on identical -meshes if neither is `BoutReal`. +Field expressions and generated operators +----------------------------------------- + +At user level, field algebra now looks more uniform than it used to: +ordinary arithmetic and many unary algebraic operators can be combined +into lazy expressions and only materialized when a concrete field or +scalar result is needed. + +This implementation is split into two layers. + +``BinaryExpr`` and views +~~~~~~~~~~~~~~~~~~~~~~~~ + +The lazy-expression layer lives in ``include/bout/fieldops.hxx``. The +central type is ``BinaryExpr``, which stores: + +- views of the left and right expression operands +- the operation functor +- mesh and metadata needed to check compatibility and materialize the + result +- a cached list of linear region indices describing where the + expression is valid + +`Field2D`, `Field3D`, and `FieldPerp` act as expression leaves by +providing lightweight ``View`` types. Those views are the device- and +backend-friendly objects used by the expression evaluator. + +Materialization happens when a field is constructed or assigned from an +expression, when an expression is stored in `Options`, or when a scalar +reduction such as ``min`` or ``mean`` is requested. The same mechanism +is also used to propagate metadata such as mesh, staggered location, +directions, and `FieldPerp` y-index. + +The unary algebraic helpers in ``include/bout/field.hxx`` build on the +same mechanism. Functions such as ``sqrt``, ``abs``, ``SQ``, +``if_else``, ``if_else_zero``, ``min``, ``max``, and ``mean`` can all +operate directly on lazy expressions. + +Generated eager operators +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The eager arithmetic operators and in-place update paths are still +generated automatically using the `Jinja`_ templating system. The main +files are: + +- ``src/field/gen_fieldops.jinja`` +- ``src/field/gen_fieldops.py`` +- ``src/field/generated_fieldops.cxx`` + +The generated code handles the broad matrix of combinations between +`BoutReal`, `Field2D`, `Field3D`, `Field3DParallel`, and `FieldPerp`, +including several mixed-rank and in-place cases where hand-maintaining +all overloads would be error-prone. + +The generated loops now also depend on the configured execution backend. +At configure time, the generator is told whether to emit RAJA-based, +OpenMP-based, or serial loop bodies for the eager paths. + +Because this is low-level code, the generated source is kept in the git +repository. Python and Jinja are therefore only needed when changing the +operator generator itself, not for an ordinary build. + +.. warning:: + + Do not edit ``generated_fieldops.cxx`` directly. Instead, modify the + template or generator, then regenerate the file and commit both the + source change and the regenerated output. To install Jinja: @@ -527,16 +567,17 @@ To install Jinja: $ pip3 install --user Jinja2 -To re-generate the code, there is a ``make`` target for -``gen_fieldops.cxx`` in ``src/field/makefile``. This also tries to -apply ``clang-format`` in order to keep to a consistent code style. +To regenerate the code, use the target for ``gen_fieldops.cxx`` in +``src/field/makefile`` or the corresponding CMake-driven generation +path. This also applies ``clang-format`` to keep the output consistent. -.. note:: ``clang-format`` is bundled with ``clang``. This should be - available through your system package manager. If you do not - have sufficient privileges on your system, you can install - it from the source `clang`_. One of the BOUT++ maintainers - can help apply it for you too. +.. note:: + + ``clang-format`` is bundled with ``clang``. This should be available + through your system package manager. If you do not have sufficient + privileges on your system, you can install it from the source + `clang`_. One of the BOUT++ maintainers can also help apply it for + you. .. _Jinja: http://jinja.pocoo.org/ .. _clang: https://clang.llvm.org/ - diff --git a/manual/sphinx/developer_docs/debugging.rst b/manual/sphinx/developer_docs/debugging.rst index 805cb5409a..4f47cd63e3 100644 --- a/manual/sphinx/developer_docs/debugging.rst +++ b/manual/sphinx/developer_docs/debugging.rst @@ -34,14 +34,77 @@ To enable the ``output_debug`` messages, configure BOUT++ with a configure BOUT++ with ``-DENABLE_OUTPUT_DEBUG``. When running BOUT++ add a ``-v -v`` flag to see ``output_debug`` messages. +Backtrace +========= + +BOUT++ can also automatically print a backtrace in the event of a crash. This is +very useful to include if you ever need to report a bug to the developers! The +output looks something like this: + +.. code:: text + + ... + Error encountered: Stack trace (most recent call first): + #0 (filtered) + #1 (filtered) + #2 (filtered) + #3 in BoutMesh::createCommunicators() + at BOUT-dev/src/mesh/impls/bout/boutmesh.cxx:709:64 + 707: } + 708: // Unconditional exception for demo purposes + > 709: throw BoutException("Single null outer SOL not correct\n"); + ^ + 710: MPI_Group_free(&group); + 711: } + #4 in BoutMesh::load() + at BOUT-dev/src/mesh/impls/bout/boutmesh.cxx:575:22 + 573: /// Communicator + 574: + > 575: createCommunicators(); + ^ + 576: output_debug << "Got communicators" << endl; + #5 in BoutInitialise(int&, char**&) + at BOUT-dev/src/bout++.cxx:201:28 + 199: bout::globals::mesh = Mesh::create(); + 200: // Load from sources. Required for Field initialisation + > 201: bout::globals::mesh->load(); + ^ + 202: + 203: // time_report options are used in BoutFinalise, i.e. after we + #6 in main + at BOUT-dev/examples/elm-pb/elm_pb.cxx:2161:1 + 2159: }; + 2160: + > 2161: BOUTMAIN(ELMpb); + ^ + #7 (filtered) + #8 (filtered) + #9 (filtered) + + ====== Exception thrown ====== + Single null outer SOL not correct + + +Debug symbols are required to get the filename/line number and code snippets. If +they are missing in either BOUT++ or the physics model, only the function name +and signature will be included for that part. + +Including debug symbols is a configure time ``CMake`` option, set either: +``-DCMAKE_BUILD_TYPE=Debug`` or ``-DCMAKE_BUILD_TYPE=RelWithDebInfo`` (the +default). + +The formatting of this backtrace is controlled in +`BoutException::getBacktrace()` using the `cpptrace +`_ library. + + Message Stack ============= -The second utility BOUT++ has to help debugging is the message stack -using the `TRACE` (and related `AUTO_TRACE`) macro. These are very -useful for when a bug only occurs after a long time of running, and/or -only occasionally. The ``TRACE`` macro can simply be dropped in -anywhere in the code:: +Another utility BOUT++ has to help debugging is the message stack using the +`TRACE` macro. This is very useful for when a bug only occurs after a long time +of running, and/or only occasionally. The ``TRACE`` macro can simply be dropped +in anywhere in the code:: { TRACE("Some message here"); // message pushed @@ -70,14 +133,15 @@ help find where an error occurred. For example, given this snippet:: we would see something like the following output: .. code:: text - - ====== Back trace ====== + + ====== Exception thrown ====== + Something went wrong + + === Additional information === -> 4. Inner-most scope on line 58 of '/path/to/model.cxx' -> 2. Middle scope on line 53 of '/path/to/model.cxx' -> 1. Outer-most scope on line 51 of '/path/to/model.cxx' - ====== Exception thrown ====== - Something went wrong The third ``TRACE`` message doesn't appear in the output because we've left its scope and it's no longer relevant. @@ -95,52 +159,29 @@ the ``fmt`` syntax also used by the loggers:: TRACE("Value of i={}, some arbitrary {}", i, "string"); -There is also an ``AUTO_TRACE`` macro that automatically captures the -name of the function it's used in. This is used throughout the main -library, especially in functions where numerical issues are likely to -arise. -Backtrace -========= +Time evolution +============== -Lastly, BOUT++ can also automatically print a backtrace in the event -of a crash. This is a compile-time option in the BOUT++ library -(``-DBOUT_ENABLE_BACKTRACE=ON``, the default, requires the -``addr2line`` program to be installed), and debug symbols to be turned -on (``-DCMAKE_BUILD_TYPE=Debug`` or ``=RelWithDebInfo``) in BOUT++ -_and_ the physics model. If debug symbols are only present in part, the -backtrace will be missing names for the other part. +It can be useful to know what happened when the simulation failed. The pvode +solver can dump the state of the simulation, at the time the solver +failed. This information includes the individual terms in the derivative. This +allows to identify which term is causing the issue. Additionally, the +residuum is dumped. This identifies not only which term is causing the issue, +but also where in the domain the solver is struggling. This can be enabled +with:: -The output looks something like this: + solver:type=pvode solver:debug_on_failure=true -.. code:: text +It can be also useful for understanding why the solver is slow. Forcing a +higher min_timestep, the solver will fail to evolve the system as it +encounters the situation, and provides information where it is happening. +This can be done with:: - ... - Error encountered - ====== Exception path ====== - [bt] #10 ./backtrace() [0x40a27e] - _start at /home/abuild/rpmbuild/BUILD/glibc-2.33/csu/../sysdeps/x86_64/start.S:122 - [bt] #9 /lib64/libc.so.6(__libc_start_main+0xd5) [0x7fecbfa28b25] - __libc_start_main at /usr/src/debug/glibc-2.33-4.1.x86_64/csu/../csu/libc-start.c:332 - [bt] #8 ./backtrace() [0x40a467] - main at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:32 (discriminator 9) - [bt] #7 /path/to/BOUT-dev/build/libbout++.so(_ZN6Solver8setModelEP12PhysicsModel+0xb5) [0x7fecc0ca2e93] - Solver::setModel(PhysicsModel*) at /path/to/BOUT-dev/build/../src/solver/solver.cxx:94 - [bt] #6 /path/to/BOUT-dev/build/libbout++.so(_ZN12PhysicsModel10initialiseEP6Solver+0xc0) [0x7fecc0cad594] - PhysicsModel::initialise(Solver*) at /path/to/BOUT-dev/build/../include/bout/physicsmodel.hxx:93 (discriminator 5) - [bt] #5 ./backtrace() [0x40a986] - Backtrace::init(bool) at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:27 - [bt] #4 ./backtrace() [0x40a3cf] - f3() at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:19 - [bt] #3 ./backtrace() [0x40a3be] - f2(int) at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:15 - [bt] #2 ./backtrace() [0x40a386] - f1() at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:13 (discriminator 2) - [bt] #1 ./backtrace(_ZN13BoutExceptionC1IA19_cJEEERKT_DpRKT0_+0xba) [0x40ae16] - BoutException::BoutException(char const (&) [19]) at /path/to/BOUT-dev/build/../include/bout/../boutexception.hxx:28 (discriminator 2) - - -This output tends to be much harder to read than the message stack -from ``TRACE`` macros, but the advantage is that it doesn't require -any modifications to the code to use, and can give you more precise -location information. + solver:type=pvode solver:debug_on_failure=true solver:min_timestep=1e2 + +It is also possible to dump at a specific time using the euler solver. +This can be useful for tracking down what is causing differences between two +different versions. It can be used with:: + + solver:type=euler solver:dump_at_time=0 input:error_on_unused_options=false diff --git a/manual/sphinx/developer_docs/performance_profiling.rst b/manual/sphinx/developer_docs/performance_profiling.rst index de14d695da..d171f403a4 100644 --- a/manual/sphinx/developer_docs/performance_profiling.rst +++ b/manual/sphinx/developer_docs/performance_profiling.rst @@ -7,13 +7,13 @@ Performance profiling ===================== Analyzing code behaviour is vital for getting the best performance from BOUT++. -This is done by profiling the code, that is, building and running the code +This is done by profiling the code, that is, building and running the code using tools that report the amount of time each processor spends in functions, on communications, etc. -This section describes how to compile and run BOUT++ using the +This section describes how to compile and run BOUT++ using the `Scorep `_/`Scalasca `_ -and +and `Extrae `_/`Paraver `_ tool chains. Both are suitable for analyzing code parallelized with MPI and/or OpenMP. @@ -29,10 +29,10 @@ Instrumentation Scorep automatically reports the time spent in MPI communications and OpenMP loops. However, to obtain information on the time spent in specific functions, -it is necessary to instrument the source code. The macros to do this are +it is necessary to instrument the source code. The macros to do this are provided in ``scorepwrapper.hxx``. -To include a function in Scorep's timing, include the scorep wrapper in the +To include a function in Scorep's timing, include the scorep wrapper in the source code .. code-block:: c++ @@ -72,8 +72,8 @@ inside ``applyBoundary`` with the name "display name". Any number of Scorep user can be used in a function; user regions can also be nested. **Caution** Instrumenting a function makes it execute more slowly. This can -result in misleading profiling information, particularly if -fast-but-frequently-called functions are instrumented. Try to instrument +result in misleading profiling information, particularly if +fast-but-frequently-called functions are instrumented. Try to instrument significant functions only. The profiling overhead in sensibly-instrumented code should be only a few @@ -111,14 +111,14 @@ When running the code, prepend the run command with ``scalasca -analyze``, e.g. $ scalasca -analyze mpirun -np 2 elm_pb The run then produces an "archive" containing profiling data in a directory -called ``scorep___sum``. To view the profiling +called ``scorep___sum``. To view the profiling information with the cube viewer, do .. code-block:: bash $ cube scorep___sum/profile.cubex -Note that Scorep does not run if doing so would produce an archive with the +Note that Scorep does not run if doing so would produce an archive with the same name as an existing archive. Therefore to rerun an executable on the same number of processors, it is necessary to move or delete the first archive. @@ -140,19 +140,19 @@ As of 23rd January 2019, the following configuration should work $ module load archer-netcdf/4.1.3 $ module load scalasca -Note that due to a bug in the ``CC`` compiler, it is necessary to modify +Note that due to a bug in the ``CC`` compiler, it is necessary to modify ``make.config`` after configuration if profiling OpenMP-parallelized code: * add the flag ``-fopenmp`` to ``BOUT_FLAGS`` -* add the flag ``--thread=omp:ancestry`` as an argument to ``scorep`` in ``CXX`` +* add the flag ``--thread=omp:ancestry`` as an argument to ``scorep`` in ``CXX`` Extrae/Paraver profiling ------------------------ `Extrae `_ is a powerful tool allowing visualization -of commumication and computation in parallel codes. It requires minimal -instrumentation; however the trace files produced can be extremely large. +of commumication and computation in parallel codes. It requires minimal +instrumentation; however the trace files produced can be extremely large. Instrumentation, configure and build ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -218,7 +218,7 @@ Reducing trace file size When trace files are very large, Paraver will prompt the user to filter or cut the file to reduce its size. -Filtering removes some information from the trace, making it small enough to +Filtering removes some information from the trace, making it small enough to open and allow the user to select a region of interest. Cutting crops the trace to a region of interest. Both operations create new trace files, and never overwrite the original trace. @@ -233,15 +233,15 @@ The following prescription should work for manipulating large trace files: c) in 'Keep States' select box for 'Running' d) in 'Keep States' select box for 'IO' e) select a min duration of 1000 - f) click 'Apply' + f) click 'Apply' 4. View 'useful duration' configuration and locate the region of interest 5. Zoom into the region of interest, and start and end the zoom on equivalent - large sections of computation (blue/green) + large sections of computation (blue/green) 6. Right click -> Run -> Cutter 7. Change the 'Input' trace file to cut from the filtered to the original one. 8. Click cut. -This produces a trace file which has all the original profiling information, +This produces a trace file which has all the original profiling information, but is much smaller as it is limited in time to a region of interest. Machine-specific installation @@ -265,6 +265,6 @@ As of 1st February 2019, the following configuration should work $ $ export CRAYPE_LINK_TYPE=dynamic -Note that due to a bug in the ``CC`` compiler, it is necessary to modify -``make.config`` after configuration to add the flag ``-fopenmp`` to +Note that due to a bug in the ``CC`` compiler, it is necessary to modify +``make.config`` after configuration to add the flag ``-fopenmp`` to ``BOUT_FLAGS``, when profiling OpenMP-parallelized code. diff --git a/manual/sphinx/figs/Topologies/CDN/CDN_Geometry.png b/manual/sphinx/figs/Topologies/CDN/CDN_Geometry.png new file mode 100644 index 0000000000..ae68ace004 Binary files /dev/null and b/manual/sphinx/figs/Topologies/CDN/CDN_Geometry.png differ diff --git a/manual/sphinx/figs/Topologies/CDN/CDN_processors.png b/manual/sphinx/figs/Topologies/CDN/CDN_processors.png new file mode 100644 index 0000000000..d949fed6fe Binary files /dev/null and b/manual/sphinx/figs/Topologies/CDN/CDN_processors.png differ diff --git a/manual/sphinx/figs/Topologies/CDN/CDN_regions.png b/manual/sphinx/figs/Topologies/CDN/CDN_regions.png new file mode 100644 index 0000000000..9bc89484e5 Binary files /dev/null and b/manual/sphinx/figs/Topologies/CDN/CDN_regions.png differ diff --git a/manual/sphinx/figs/Topologies/CDN/CDN_topology.png b/manual/sphinx/figs/Topologies/CDN/CDN_topology.png new file mode 100644 index 0000000000..a6986108d4 Binary files /dev/null and b/manual/sphinx/figs/Topologies/CDN/CDN_topology.png differ diff --git a/manual/sphinx/figs/Topologies/SF/SF_processors.png b/manual/sphinx/figs/Topologies/SF/SF_processors.png new file mode 100644 index 0000000000..e4f9cd22d3 Binary files /dev/null and b/manual/sphinx/figs/Topologies/SF/SF_processors.png differ diff --git a/manual/sphinx/figs/Topologies/SF/SF_regions.png b/manual/sphinx/figs/Topologies/SF/SF_regions.png new file mode 100644 index 0000000000..2f423b7ce9 Binary files /dev/null and b/manual/sphinx/figs/Topologies/SF/SF_regions.png differ diff --git a/manual/sphinx/figs/Topologies/SF/SF_topology.png b/manual/sphinx/figs/Topologies/SF/SF_topology.png new file mode 100644 index 0000000000..975f54d1bd Binary files /dev/null and b/manual/sphinx/figs/Topologies/SF/SF_topology.png differ diff --git a/manual/sphinx/figs/Topologies/SF/SFminus_Geometry.png b/manual/sphinx/figs/Topologies/SF/SFminus_Geometry.png new file mode 100644 index 0000000000..6b4dc293a8 Binary files /dev/null and b/manual/sphinx/figs/Topologies/SF/SFminus_Geometry.png differ diff --git a/manual/sphinx/figs/Topologies/SF/SFplus_Geometry.png b/manual/sphinx/figs/Topologies/SF/SFplus_Geometry.png new file mode 100644 index 0000000000..0a09de9bef Binary files /dev/null and b/manual/sphinx/figs/Topologies/SF/SFplus_Geometry.png differ diff --git a/manual/sphinx/figs/Topologies/SN/SN_Geometry.png b/manual/sphinx/figs/Topologies/SN/SN_Geometry.png new file mode 100644 index 0000000000..8baf5ac9a5 Binary files /dev/null and b/manual/sphinx/figs/Topologies/SN/SN_Geometry.png differ diff --git a/manual/sphinx/figs/Topologies/SN/SN_processors.png b/manual/sphinx/figs/Topologies/SN/SN_processors.png new file mode 100644 index 0000000000..593ff022e3 Binary files /dev/null and b/manual/sphinx/figs/Topologies/SN/SN_processors.png differ diff --git a/manual/sphinx/figs/Topologies/SN/SN_regions.png b/manual/sphinx/figs/Topologies/SN/SN_regions.png new file mode 100644 index 0000000000..d4cfc97d37 Binary files /dev/null and b/manual/sphinx/figs/Topologies/SN/SN_regions.png differ diff --git a/manual/sphinx/figs/Topologies/SN/SN_topology.png b/manual/sphinx/figs/Topologies/SN/SN_topology.png new file mode 100644 index 0000000000..c3ae497618 Binary files /dev/null and b/manual/sphinx/figs/Topologies/SN/SN_topology.png differ diff --git a/manual/sphinx/figs/Topologies/UDN/UDN_Geometry.png b/manual/sphinx/figs/Topologies/UDN/UDN_Geometry.png new file mode 100644 index 0000000000..e4870212dd Binary files /dev/null and b/manual/sphinx/figs/Topologies/UDN/UDN_Geometry.png differ diff --git a/manual/sphinx/figs/Topologies/UDN/UDN_processors.png b/manual/sphinx/figs/Topologies/UDN/UDN_processors.png new file mode 100644 index 0000000000..b65a02c203 Binary files /dev/null and b/manual/sphinx/figs/Topologies/UDN/UDN_processors.png differ diff --git a/manual/sphinx/figs/Topologies/UDN/UDN_regions.png b/manual/sphinx/figs/Topologies/UDN/UDN_regions.png new file mode 100644 index 0000000000..4f2657341c Binary files /dev/null and b/manual/sphinx/figs/Topologies/UDN/UDN_regions.png differ diff --git a/manual/sphinx/figs/Topologies/UDN/UDN_topology.png b/manual/sphinx/figs/Topologies/UDN/UDN_topology.png new file mode 100644 index 0000000000..8929be78d5 Binary files /dev/null and b/manual/sphinx/figs/Topologies/UDN/UDN_topology.png differ diff --git a/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb b/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb index c3c39a3c12..4e940b998f 100644 --- a/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb +++ b/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb @@ -40,10 +40,10 @@ "outputs": [], "source": [ "# Note, these can be max 9 due to the current index convention\n", - "nx = 3 # Not including ghost points\n", + "nx = 3 # Not including ghost points\n", "nz = 3\n", "\n", - "startXIndex = 10 # The x indices are the slowest growing indices\n", + "startXIndex = 10 # The x indices are the slowest growing indices\n", "startZIndex = 1 # The z indices are the fastest growing indices" ] }, @@ -85,13 +85,13 @@ "for z in range(nz):\n", " f.append([])\n", " xStart = startZIndex\n", - " xEnd = startXIndex*(nx+1) # +1 due to ghost point\n", + " xEnd = startXIndex * (nx + 1) # +1 due to ghost point\n", " # +startXIndex in the range as the range does not include endpoint\n", - " for xInd in range(xStart, xEnd+startXIndex, 10):\n", - " ind = str(xInd+z)\n", - " if (xInd+z) < startXIndex:\n", - " ind = '0'+str(xInd+z)\n", - " f[z].append(symbols('f_' + ind))\n", + " for xInd in range(xStart, xEnd + startXIndex, 10):\n", + " ind = str(xInd + z)\n", + " if (xInd + z) < startXIndex:\n", + " ind = \"0\" + str(xInd + z)\n", + " f[z].append(symbols(\"f_\" + ind))\n", "\n", "mesh = Matrix(f[::-1])\n", "display(mesh)" @@ -195,21 +195,21 @@ } ], "source": [ - "xVec=[]\n", - "bVec=[]\n", + "xVec = []\n", + "bVec = []\n", "# Do the inner loop, so start ranges at 1\n", "# (nx+1) to include outer ghost point, +1 in the range as the range does not include endpoint\n", - "for x in range(1, (nx+1)+1):\n", - " for z in range(1,nz+1):\n", - " xVec.append(symbols('x_'+str(x)+'_'+str(z)))\n", - " bVec.append(symbols('b_'+str(x)+'_'+str(z)))\n", + "for x in range(1, (nx + 1) + 1):\n", + " for z in range(1, nz + 1):\n", + " xVec.append(symbols(\"x_\" + str(x) + \"_\" + str(z)))\n", + " bVec.append(symbols(\"b_\" + str(x) + \"_\" + str(z)))\n", "\n", "# Do the inner ghost points\n", "# Must count backwards since we are inserting in the front\n", - "for ind in range(nz,0,-1):\n", - " xVec.insert(0, symbols('x_0_'+str(ind)))\n", - " bVec.insert(0, symbols('b_0_'+str(ind)))\n", - " \n", + "for ind in range(nz, 0, -1):\n", + " xVec.insert(0, symbols(\"x_0_\" + str(ind)))\n", + " bVec.insert(0, symbols(\"b_0_\" + str(ind)))\n", + "\n", "display(Matrix(xVec))\n", "display(Matrix(bVec))" ] @@ -274,7 +274,7 @@ "globInd = []\n", "for rows in range(len(xVec)):\n", " cols = []\n", - " for col in range(rows*len(xVec), (rows+1)*len(xVec)):\n", + " for col in range(rows * len(xVec), (rows + 1) * len(xVec)):\n", " cols.append(col)\n", " globInd.append(cols)\n", "\n", @@ -305,10 +305,14 @@ "source": [ "c = []\n", "for x in range(nx):\n", - " indexStart = (startXIndex+1)+(startXIndex*x) # Multiply by 10 due to index system\n", - " indexEnd = (startXIndex+nz+1)+(startXIndex*x) # Multiply by 10 due to index system\n", + " indexStart = (startXIndex + 1) + (\n", + " startXIndex * x\n", + " ) # Multiply by 10 due to index system\n", + " indexEnd = (startXIndex + nz + 1) + (\n", + " startXIndex * x\n", + " ) # Multiply by 10 due to index system\n", " for ind in range(indexStart, indexEnd):\n", - " c.append(symbols('c_'+str(ind)))" + " c.append(symbols(\"c_\" + str(ind)))" ] }, { @@ -328,11 +332,11 @@ "source": [ "# The inner ghost\n", "innerGhostStart = startZIndex\n", - "innerGhostEnd = nz\n", + "innerGhostEnd = nz\n", "ig = []\n", "# +1 in the range as last point is not included\n", - "for z in range(innerGhostStart, innerGhostEnd+1):\n", - " ig.append(symbols('ig_0_'+str(z)))" + "for z in range(innerGhostStart, innerGhostEnd + 1):\n", + " ig.append(symbols(\"ig_0_\" + str(z)))" ] }, { @@ -345,12 +349,12 @@ "source": [ "# The outer ghost\n", "# nx+1 as we want to go past the last inner x grid point\n", - "outerGhostStart = startXIndex*(nx+1) + startZIndex\n", - "outerGhostEnd = startXIndex*(nx+1) + nz\n", + "outerGhostStart = startXIndex * (nx + 1) + startZIndex\n", + "outerGhostEnd = startXIndex * (nx + 1) + nz\n", "og = []\n", "# +1 in the range as last point is not included\n", - "for z in range(outerGhostStart, outerGhostEnd+1):\n", - " og.append(symbols('og_'+str(z)))" + "for z in range(outerGhostStart, outerGhostEnd + 1):\n", + " og.append(symbols(\"og_\" + str(z)))" ] }, { @@ -396,21 +400,27 @@ "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # The last -1 is there as the matrix indices count from 0\n", - " startRow = (nz+1)+(x*nz)-1 # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = (nz+1)+(x*nz)+(nz-1)-1 # Ending row-1 before the last z-index (last z will be wrapped around)\n", + " startRow = (\n", + " (nz + 1) + (x * nz) - 1\n", + " ) # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = (\n", + " (nz + 1) + (x * nz) + (nz - 1) - 1\n", + " ) # Ending row-1 before the last z-index (last z will be wrapped around)\n", " # +1 in range as last point is not included\n", - " rows = range(startRow, endRow+1)\n", - " cols = range(startRow+1, endRow+1) # Column is shifted +1 from the diagonal\n", - " \n", + " rows = range(startRow, endRow + 1)\n", + " cols = range(startRow + 1, endRow + 1) # Column is shifted +1 from the diagonal\n", + "\n", " # The indices referring to the spatial point in the grid\n", " # The last \"+1\" is fue to the fact that the column is shifted +1 from the diagonal\n", - " startInd = (startXIndex+startZIndex) + (startXIndex*x) + 1\n", - " endInd = (startXIndex+startZIndex) + (nz-1) + (startXIndex*x) + 1 # Wrap around last point\n", + " startInd = (startXIndex + startZIndex) + (startXIndex * x) + 1\n", + " endInd = (\n", + " (startXIndex + startZIndex) + (nz - 1) + (startXIndex * x) + 1\n", + " ) # Wrap around last point\n", " # +1 in range as last point is not included\n", - " inds = range(startInd, endInd+1)\n", - " \n", + " inds = range(startInd, endInd + 1)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('zp_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"zp_\" + str(ind))" ] }, { @@ -423,15 +433,17 @@ "source": [ "# The wrap around\n", "# The index referring to the spatial point in the grid\n", - "startInd = startXIndex+startZIndex\n", + "startInd = startXIndex + startZIndex\n", "# The indices referring to the matrix index\n", - "# Last -1 as the matrix indices are counted from 0 \n", - "startRow = (nz+1) + (nz-1) - 1 # nz+1 below from the ghost sub matrix, nz-1 below after that\n", - "startCol = (nz+1)-1 # nz+1 left of the ghost sub matrix\n", + "# Last -1 as the matrix indices are counted from 0\n", + "startRow = (\n", + " (nz + 1) + (nz - 1) - 1\n", + ") # nz+1 below from the ghost sub matrix, nz-1 below after that\n", + "startCol = (nz + 1) - 1 # nz+1 left of the ghost sub matrix\n", "for wrap in range(nx):\n", - " row = startRow+wrap*nz\n", - " col = startCol+wrap*nz\n", - " InvM[row, col] = symbols('zp_'+str(startInd+startXIndex*wrap))" + " row = startRow + wrap * nz\n", + " col = startCol + wrap * nz\n", + " InvM[row, col] = symbols(\"zp_\" + str(startInd + startXIndex * wrap))" ] }, { @@ -452,20 +464,26 @@ "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # The last -1 is there as the matrix indices count from 0\n", - " startRow = (nz+1)+(x*nz)-1 # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = (nz+1)+(x*nz)+(nz-1)-1 # Ending row-1 before the last z-index (last z will be wrapped around)\n", + " startRow = (\n", + " (nz + 1) + (x * nz) - 1\n", + " ) # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = (\n", + " (nz + 1) + (x * nz) + (nz - 1) - 1\n", + " ) # Ending row-1 before the last z-index (last z will be wrapped around)\n", " # +1 in range as last point is not included\n", - " rows = range(startRow+1, endRow+1) # Row is shifted +1 from the diagonal\n", - " cols = range(startRow, endRow+1)\n", - " \n", + " rows = range(startRow + 1, endRow + 1) # Row is shifted +1 from the diagonal\n", + " cols = range(startRow, endRow + 1)\n", + "\n", " # The indices referring to the spatial point in the grid\n", - " startInd = (startXIndex+startZIndex) + (startXIndex*x)\n", - " endInd = (startXIndex+startZIndex) + (nz-1) + (startXIndex*x) # Wrap around last point\n", + " startInd = (startXIndex + startZIndex) + (startXIndex * x)\n", + " endInd = (\n", + " (startXIndex + startZIndex) + (nz - 1) + (startXIndex * x)\n", + " ) # Wrap around last point\n", " # +1 in range as last point is not included\n", - " inds = range(startInd, endInd+1)\n", - " \n", + " inds = range(startInd, endInd + 1)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('zm_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"zm_\" + str(ind))" ] }, { @@ -478,15 +496,19 @@ "source": [ "# The wrap around\n", "# The index referring to the spatial point in the grid\n", - "startInd = startXIndex+startZIndex+(nz-1) # +(nz-1) as this will be the last z point for the current x\n", + "startInd = (\n", + " startXIndex + startZIndex + (nz - 1)\n", + ") # +(nz-1) as this will be the last z point for the current x\n", "# The indices referring to the matrix index\n", - "# Last -1 as the matrix indices are counted from 0 \n", - "startRow = (nz+1)-1 # nz+1 below the ghost sub matrix\n", - "startCol = (nz+1) + (nz-1) - 1 # nz+1 left from the ghost sub matrix, nz-1 left after that\n", + "# Last -1 as the matrix indices are counted from 0\n", + "startRow = (nz + 1) - 1 # nz+1 below the ghost sub matrix\n", + "startCol = (\n", + " (nz + 1) + (nz - 1) - 1\n", + ") # nz+1 left from the ghost sub matrix, nz-1 left after that\n", "for wrap in range(nx):\n", - " row = startRow+wrap*nz\n", - " col = startCol+wrap*nz\n", - " InvM[row, col] = symbols('zm_'+str(startInd+startXIndex*wrap))" + " row = startRow + wrap * nz\n", + " col = startCol + wrap * nz\n", + " InvM[row, col] = symbols(\"zm_\" + str(startInd + startXIndex * wrap))" ] }, { @@ -505,23 +527,29 @@ "outputs": [], "source": [ "# Indices referring to the spatial points in the grid\n", - "startInd = startXIndex*2 + startZIndex # *2 as we start at the second inner x-index\n", - "endInd = startInd + (startZIndex*nz) # *nz as this is the last z-index in the current x-index\n", + "startInd = startXIndex * 2 + startZIndex # *2 as we start at the second inner x-index\n", + "endInd = startInd + (\n", + " startZIndex * nz\n", + ") # *nz as this is the last z-index in the current x-index\n", "\n", "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # The last -1 as the matrix indices counts from 0\n", - " startRow = (nz+1)+(x*nz)-1 # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = (nz+1)+(x*nz)+(nz)-1 # Ending at the row referring to the last z-index\n", + " startRow = (\n", + " (nz + 1) + (x * nz) - 1\n", + " ) # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = (\n", + " (nz + 1) + (x * nz) + (nz) - 1\n", + " ) # Ending at the row referring to the last z-index\n", " # Not +1 in range as we do not want to include last point\n", - " rows = range(startRow, endRow)\n", - " cols = range(startRow+nz, endRow+nz) # Start at first index after last z-index\n", - " \n", + " rows = range(startRow, endRow)\n", + " cols = range(startRow + nz, endRow + nz) # Start at first index after last z-index\n", + "\n", " # Indices referring to the spatial points in the grid\n", - " inds = range(startInd+startXIndex*x, endInd+startXIndex*x)\n", - " \n", + " inds = range(startInd + startXIndex * x, endInd + startXIndex * x)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('xp_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"xp_\" + str(ind))" ] }, { @@ -534,22 +562,22 @@ "source": [ "# x+1 for inner ghost point\n", "# Indices referring to the spatial points in the grid\n", - "startInd = startXIndex + startZIndex # First inner point for first z\n", - "endInd = startInd + (startZIndex*nz) # First inner point for last z\n", + "startInd = startXIndex + startZIndex # First inner point for first z\n", + "endInd = startInd + (startZIndex * nz) # First inner point for last z\n", "\n", "# The indices referring to the matrix index\n", "# The last -1 as the matrix indices counts from 0\n", - "startRow = startZIndex-1 # Starting at first row\n", - "endRow = startZIndex+nz-1 # Ending at the row referring to the last z-index\n", + "startRow = startZIndex - 1 # Starting at first row\n", + "endRow = startZIndex + nz - 1 # Ending at the row referring to the last z-index\n", "# Not +1 in range as we do not want to include last point\n", - "rows = range(startRow, endRow)\n", - "cols = range(startRow+nz, endRow+nz) # Start at first index after last z-index\n", - " \n", + "rows = range(startRow, endRow)\n", + "cols = range(startRow + nz, endRow + nz) # Start at first index after last z-index\n", + "\n", "# Indices referring to the spatial points in the grid\n", - "inds = range(startInd, endInd)\n", - " \n", + "inds = range(startInd, endInd)\n", + "\n", "for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('igxp_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"igxp_\" + str(ind))" ] }, { @@ -569,25 +597,25 @@ "source": [ "# Indices referring to the spatial points in the grid\n", "startInd = startZIndex\n", - "endInd = startInd + nz\n", + "endInd = startInd + nz\n", "\n", "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # Note that x starts counting from zero, so we must add 1 to x in the rows\n", - " startRow = ((x+1)*nz) # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = ((x+1)*nz)+(nz) # Ending at the row referring to the last z-index\n", + " startRow = (x + 1) * nz # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = ((x + 1) * nz) + (nz) # Ending at the row referring to the last z-index\n", " # Not +1 in range as we do not want to include last point\n", - " rows = range(startRow, endRow)\n", - " cols = range(startRow-nz, endRow-nz) # Start at first index after last z-index\n", - " \n", + " rows = range(startRow, endRow)\n", + " cols = range(startRow - nz, endRow - nz) # Start at first index after last z-index\n", + "\n", " # Indices referring to the spatial points in the grid\n", - " inds = range(startInd+startXIndex*x, endInd+startXIndex*x)\n", - " \n", + " inds = range(startInd + startXIndex * x, endInd + startXIndex * x)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", " if (ind) < startXIndex:\n", - " ind = '0'+str(ind)\n", + " ind = \"0\" + str(ind)\n", "\n", - " InvM[rInd, cInd] = symbols('xm_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"xm_\" + str(ind))" ] }, { @@ -600,22 +628,24 @@ "source": [ "# x-1 for inner ghost point\n", "# Indices referring to the spatial points in the grid\n", - "startInd = startXIndex*nx + startZIndex # Last inner point for first z\n", - "endInd = startInd + (startZIndex*nz) # Last inner point for last z\n", + "startInd = startXIndex * nx + startZIndex # Last inner point for first z\n", + "endInd = startInd + (startZIndex * nz) # Last inner point for last z\n", "\n", "# The indices referring to the matrix index\n", "# The last -1 as the matrix indices counts from 0\n", - "startRow = len(xVec)-nz-1 # Starting at last inner point row\n", - "endRow = len(xVec)-1 # Ending at the last row\n", + "startRow = len(xVec) - nz - 1 # Starting at last inner point row\n", + "endRow = len(xVec) - 1 # Ending at the last row\n", "# +1 in range as last point is not included\n", - "rows = range(startRow+1, endRow+1)\n", - "cols = range(startRow-nz+1, endRow-nz+1) # Start at first index after last z-index\n", - " \n", + "rows = range(startRow + 1, endRow + 1)\n", + "cols = range(\n", + " startRow - nz + 1, endRow - nz + 1\n", + ") # Start at first index after last z-index\n", + "\n", "# Indices referring to the spatial points in the grid\n", - "inds = range(startInd, endInd)\n", - " \n", + "inds = range(startInd, endInd)\n", + "\n", "for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('ogxm_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"ogxm_\" + str(ind))" ] }, { diff --git a/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py b/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py index 8a95a13436..5a4a2a0843 100644 --- a/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py +++ b/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py @@ -10,10 +10,10 @@ mmag@fysik.dtu.dk """ -__authors__ = 'Michael Loeiten' -__email__ = 'mmag@fysik.dtu.dk' -__version__ = '1.0' -__date__ = '21.01.2016' +__authors__ = "Michael Loeiten" +__email__ = "mmag@fysik.dtu.dk" +__version__ = "1.0" +__date__ = "21.01.2016" import pygraphviz as pgv @@ -21,113 +21,118 @@ tree = pgv.AGraph() # Appendable lists -files = [] +files = [] dead_ends = [] # Default node attributes -tree.node_attr['shape']='box' -tree.node_attr['style']='bold' +tree.node_attr["shape"] = "box" +tree.node_attr["style"] = "bold" # Adding nodes and edges -l0 = 'project' -l1 = ['data', 'source\nfiles', 'driver.py'] +l0 = "project" +l1 = ["data", "source\nfiles", "driver.py"] # Append the files files.append(l1[1]) files.append(l1[2]) # Add the boxes to the mother node for box in l1: - tree.add_edge(l0,box) + tree.add_edge(l0, box) -l2 = ['solver1', 'solver2',\ - 'BOUT.inp', 'run_log.txt'] +l2 = ["solver1", "solver2", "BOUT.inp", "run_log.txt"] # Append the files files.append(l2[2]) files.append(l2[3]) # Add the boxes to the mother node for box in l2: - tree.add_edge('data', box) -tree.add_edge('solver2', 'solver2/...') + tree.add_edge("data", box) +tree.add_edge("solver2", "solver2/...") # Append the dead_end -de = l2[1] + '/...' +de = l2[1] + "/..." dead_ends.append(de) -l3 = ['method1', 'method2', 'solver1/...'] +l3 = ["method1", "method2", "solver1/..."] for box in l3: - tree.add_edge('solver1', box) -tree.add_edge('method2', 'method2/...') + tree.add_edge("solver1", box) +tree.add_edge("method2", "method2/...") # Append the dead_end de = l3[2] dead_ends.append(de) -de = l3[1] + '/...' +de = l3[1] + "/..." dead_ends.append(de) -l4 = ['nout\ntimestep1', 'nout\ntimestep2', 'method1/...'] +l4 = ["nout\ntimestep1", "nout\ntimestep2", "method1/..."] for box in l4: - tree.add_edge('method1', box) -tree.add_edge('nout\ntimestep2', 'nout\ntimestep2/...') + tree.add_edge("method1", box) +tree.add_edge("nout\ntimestep2", "nout\ntimestep2/...") # Append the dead_end de = l4[2] dead_ends.append(de) -de = l4[1] + '/...' +de = l4[1] + "/..." dead_ends.append(de) -l5 = ['mesh1', 'mesh2', 'nout\ntimestep1/...'] +l5 = ["mesh1", "mesh2", "nout\ntimestep1/..."] for box in l5: - tree.add_edge('nout\ntimestep1', box) -tree.add_edge('mesh2', 'mesh2/...') + tree.add_edge("nout\ntimestep1", box) +tree.add_edge("mesh2", "mesh2/...") # Append the dead_end de = l5[2] dead_ends.append(de) -de = l5[1] + '/...' +de = l5[1] + "/..." dead_ends.append(de) -l6 = ['additional1', 'additional2', 'mesh1/...'] +l6 = ["additional1", "additional2", "mesh1/..."] for box in l6: - tree.add_edge('mesh1', box) -tree.add_edge('additional2', 'additional2/...') + tree.add_edge("mesh1", box) +tree.add_edge("additional2", "additional2/...") # Append the dead_end de = l6[2] dead_ends.append(de) -de = l6[1] + '/...' +de = l6[1] + "/..." dead_ends.append(de) -l7 = ['grid_file1', 'grid_file2', 'additional1/...'] +l7 = ["grid_file1", "grid_file2", "additional1/..."] for box in l7: - tree.add_edge('additional1', box) -tree.add_edge('grid_file2', 'grid_file2/...') + tree.add_edge("additional1", box) +tree.add_edge("grid_file2", "grid_file2/...") # Append the dead_end de = l7[2] dead_ends.append(de) -de = l7[1] + '/...' +de = l7[1] + "/..." dead_ends.append(de) -l8 = ['BOUT.inp\n(copy)', 'BOUT.log', 'BOUT.dmp',\ - 'BOUT.restart', '(source_files\n(copy))', '(grid_file\n(copy))'] +l8 = [ + "BOUT.inp\n(copy)", + "BOUT.log", + "BOUT.dmp", + "BOUT.restart", + "(source_files\n(copy))", + "(grid_file\n(copy))", +] # Add l8 to the files list for cur_file in l8: files.append(cur_file) # Append them to the mother node for box in l8: - tree.add_edge('grid_file1', box) + tree.add_edge("grid_file1", box) # Change colors for the files for the_file in files: - member=tree.get_node(the_file) -# member.attr['fontcolor'] = 'limegreen' - member.attr['color'] = 'limegreen' + member = tree.get_node(the_file) + # member.attr['fontcolor'] = 'limegreen' + member.attr["color"] = "limegreen" # Change colors for the dead_ends for dead_end in dead_ends: - member=tree.get_node(dead_end) -# member.attr['fontcolor'] = 'darksalmon' - member.attr['color'] = 'darksalmon' + member = tree.get_node(dead_end) + # member.attr['fontcolor'] = 'darksalmon' + member.attr["color"] = "darksalmon" # Print the graph print(tree.string()) # Set layout -tree.layout('dot') +tree.layout("dot") # Write to file -tree.draw('folder_tree.svg') +tree.draw("folder_tree.svg") diff --git a/manual/sphinx/figs/topology_cross_section.png b/manual/sphinx/figs/topology_cross_section.png index f1cbd72b84..fdabaf9da3 100644 Binary files a/manual/sphinx/figs/topology_cross_section.png and b/manual/sphinx/figs/topology_cross_section.png differ diff --git a/manual/sphinx/index.rst b/manual/sphinx/index.rst index c150273285..6a5a48ba34 100644 --- a/manual/sphinx/index.rst +++ b/manual/sphinx/index.rst @@ -15,10 +15,12 @@ The documentation is divided into the following sections: * :ref:`model-outputs` * :ref:`bout-interfaces` - + + * :ref:`performance-and-accelerators` + * :ref:`developer-docs` - + .. toctree:: :maxdepth: 2 :caption: Getting started @@ -30,25 +32,24 @@ The documentation is divided into the following sections: user_docs/advanced_install user_docs/running_bout user_docs/new_in_v5 - + .. toctree:: :maxdepth: 2 :caption: BOUT++ models :name: bout-models - + user_docs/physics_models user_docs/makefiles user_docs/variable_init user_docs/boundary_options user_docs/testing - user_docs/gpu_support user_docs/adios2 - + .. toctree:: :maxdepth: 2 :caption: Model inputs :name: model-inputs - + user_docs/bout_options user_docs/input_grids @@ -56,19 +57,20 @@ The documentation is divided into the following sections: :maxdepth: 2 :caption: Model outputs :name: model-outputs - + user_docs/output_and_post user_docs/python_boutpp - + .. toctree:: :maxdepth: 2 :caption: BOUT++ interfaces :name: bout-interfaces - + user_docs/time_integration user_docs/parallel-transforms user_docs/laplacian user_docs/differential_operators + user_docs/field_expressions user_docs/algebraic_operators user_docs/staggered_grids user_docs/eigenvalue_solver @@ -76,6 +78,13 @@ The documentation is divided into the following sections: user_docs/invertable_operator user_docs/petsc +.. toctree:: + :maxdepth: 2 + :caption: Performance and accelerators + :name: performance-and-accelerators + + user_docs/gpu_support + .. toctree:: :maxdepth: 1 :caption: Field-aligned coordinate systems @@ -85,6 +94,14 @@ The documentation is divided into the following sections: user_docs/preconditioning user_docs/BOUT_Gradperp_op +.. toctree:: + :maxdepth: 1 + :caption: Topology Handling + :name: topologies + + user_docs/topology + user_docs/supported_topologies + .. toctree:: :maxdepth: 1 :caption: Developer Documentation diff --git a/manual/sphinx/requirements.txt b/manual/sphinx/requirements.txt index e09943ad07..52a1764023 100644 --- a/manual/sphinx/requirements.txt +++ b/manual/sphinx/requirements.txt @@ -1,4 +1,4 @@ -breathe>=4.30 +#breathe>=4.30 Jinja2>=2.11.3 numpy>=1.21.1 netcdf4>=1.5.6 diff --git a/manual/sphinx/user_docs/advanced_install.rst b/manual/sphinx/user_docs/advanced_install.rst index 048a26a6e3..f8e594be07 100644 --- a/manual/sphinx/user_docs/advanced_install.rst +++ b/manual/sphinx/user_docs/advanced_install.rst @@ -355,7 +355,7 @@ BOUT++ can use PETSc https://www.mcs.anl.gov/petsc/ for time-integration and for solving elliptic problems, such as inverting Poisson and Helmholtz equations. -Currently, BOUT++ supports PETSc versions 3.7 - 3.19. More recent versions may +Currently, BOUT++ supports PETSc versions 3.7 - 3.23. More recent versions may well work, but the PETSc API does sometimes change in backward-incompatible ways, so this is not guaranteed. To install PETSc version 3.19, use the following steps:: diff --git a/manual/sphinx/user_docs/algebraic_operators.rst b/manual/sphinx/user_docs/algebraic_operators.rst index b2089f9ec3..b8c40d4dc5 100644 --- a/manual/sphinx/user_docs/algebraic_operators.rst +++ b/manual/sphinx/user_docs/algebraic_operators.rst @@ -1,28 +1,39 @@ .. _sec-algebraic-ops: Algebraic operators -========================= +=================== BOUT++ provides a wide variety of algebraic operators acting on fields. -The algebraic operators are listed in :numref:`tab-algebraic-ops`. -For a completely up-to-date list, see the ``Non-member functions`` -part of :doc:`field2d.hxx<../_breathe_autogen/file/field2d_8hxx>`, -:doc:`field3d.hxx<../_breathe_autogen/file/field3d_8hxx>`, +Most of these operators can participate in the lazy field-expression +system described in :ref:`sec-field-expressions`. In practice this means +you can usually write ordinary algebraic code and let BOUT++ delay +evaluation until assignment or reduction. + +For a completely up-to-date list, see the ``Non-member functions`` part +of :doc:`field2d.hxx<../_breathe_autogen/file/field2d_8hxx>`, +:doc:`field3d.hxx<../_breathe_autogen/file/field3d_8hxx>`, and :doc:`fieldperp.hxx<../_breathe_autogen/file/fieldperp_8hxx>`. +Common operators +---------------- + .. _tab-algebraic-ops: .. table:: Algebraic operators - +------------------------------------------+------------------------------------------------------+ - | Name | Description | + +------------------------------------------+------------------------------------------------------+ + | Name | Description | +==========================================+======================================================+ - | ``min(f, allpe=true, region)`` | Minimum (optionally over all processes) | + | ``min(f, allpe=true, region)`` | Minimum (optionally over all processes) | +------------------------------------------+------------------------------------------------------+ | ``max(f, allpe=true, region)`` | Maximum (optionally over all processes) | +------------------------------------------+------------------------------------------------------+ + | ``mean(f, allpe=true, region)`` | Mean (optionally over all processes) | + +------------------------------------------+------------------------------------------------------+ | ``pow(lhs, rhs, region)`` | :math:`\mathtt{lhs}^\mathtt{rhs}` | +------------------------------------------+------------------------------------------------------+ + | ``SQ(f, region)`` | Square of ``f`` | + +------------------------------------------+------------------------------------------------------+ | ``sqrt(f, region)`` | :math:`\sqrt{(f)}` | +------------------------------------------+------------------------------------------------------+ | ``abs(f, region)`` | :math:`|f|` | @@ -65,24 +76,43 @@ part of :doc:`field2d.hxx<../_breathe_autogen/file/field2d_8hxx>`, | | of `f` as opposed to the AC, alternating current, or | | | fluctuating part) | +------------------------------------------+------------------------------------------------------+ + | ``if_else(cond, lhs, rhs)`` | Select between two algebraic branches | + +------------------------------------------+------------------------------------------------------+ + | ``if_else_zero(cond, expr)`` | Select either ``expr`` or zero | + +------------------------------------------+------------------------------------------------------+ + +These operators can usually be combined directly in expressions:: + + Field3D rhs = sqrt(SQ(n) + SQ(T)); + Field3D masked = if_else(use_drive, source * profile, sink * profile); + BoutReal max_error = max(abs(lhs - rhs), true); + +Reductions such as ``min``, ``max``, and ``mean`` can operate directly +on an expression, so an intermediate field is often unnecessary. + +Region arguments +---------------- -These operators take a ``region`` argument, whose values can be [#]_ (see -:ref:`sec-iterating`) +These operators take a ``region`` argument. Common values are [#]_ (see +:ref:`sec-iterating`): -- `RGN_ALL`, which is the whole mesh; +- ``RGN_ALL``, which is the whole mesh +- ``RGN_NOBNDRY``, which skips all boundaries +- ``RGN_NOX``, which skips the x boundaries +- ``RGN_NOY``, which skips the y boundaries -- `RGN_NOBNDRY`, which skips all boundaries; +The default is usually ``RGN_ALL``. Restricting the region can improve +performance when guard-cell values will not be used. -- `RGN_NOX`, which skips the x boundaries +When a region-limited expression is materialized into a field, only the +selected region is guaranteed to contain valid values. This is the same +performance-oriented convention used by other field operators. -- `RGN_NOY`, which skips the y boundaries +Further reading +--------------- -The default value for the region argument is `RGN_ALL` which should work in all -cases. However, the region argument can be used for optimization, to skip -calculations in guard cells if it is known that those results will not be -needed (for example, if no derivatives of the result will be calculated). Since -these operators can be relatively expensive compared to addition, subtraction, -multiplication this can be a useful performance improvement. +- :ref:`sec-field-expressions` +- :ref:`sec-gpusupport` -.. [#] More regions may be added in future, for example to act on only subsets of the - physical domain. +.. [#] More regions may be added in future, for example to act on only + subsets of the physical domain. diff --git a/manual/sphinx/user_docs/boundary_options.rst b/manual/sphinx/user_docs/boundary_options.rst index d5e1b9199c..f46b25f72e 100644 --- a/manual/sphinx/user_docs/boundary_options.rst +++ b/manual/sphinx/user_docs/boundary_options.rst @@ -147,8 +147,10 @@ shifted``, see :ref:`sec-shifted-metric`), the recommended method is to apply boundary conditions directly to the ``yup`` and ``ydown`` parallel slices. This can be done by setting ``bndry_par_yup`` and ``bndry_par_ydown``, or ``bndry_par_all`` to set both at once. The -possible values are ``parallel_dirichlet``, ``parallel_dirichlet_O3`` -and ``parallel_neumann``. The stencils used are the same as for the +possible values are ``parallel_dirichlet_o1``, +``parallel_dirichlet_o2``, ``parallel_dirichlet_o3`` +and ``parallel_neumann_o1``, ``parallel_neumann_o2``, +``parallel_neumann_o3``. The stencils used are the same as for the standard boundary conditions without the ``parallel_`` prefix, but are applied directly to parallel slices. The boundary condition can only be applied after the parallel slices are calculated, which is usually @@ -168,7 +170,7 @@ For example, for an evolving variable ``f``, put a section in the [f] bndry_xin = dirichlet bndry_xout = dirichlet - bndry_par_all = parallel_neumann + bndry_par_all = parallel_neumann_o2 bndry_ydown = none bndry_yup = none @@ -278,7 +280,7 @@ cells of the base variable. For example, for an evolving variable [f] bndry_xin = dirichlet bndry_xout = dirichlet - bndry_par_all = parallel_dirichlet + bndry_par_all = parallel_dirichlet_o2 bndry_ydown = none bndry_yup = none @@ -289,7 +291,7 @@ communication, while the perpendicular ones before: f.applyBoundary(); mesh->communicate(f); - f.applyParallelBoundary("parallel_neumann"); + f.applyParallelBoundary("parallel_neumann_o2"); Note that during grid generation care has to be taken to ensure that there are no "short" connection lengths. Otherwise it can happen that for a point on a @@ -434,6 +436,128 @@ the upper Y boundary of a 2D variable ``var``:: The `BoundaryRegion` class is defined in ``include/boundary_region.hxx`` +Y-Boundaries +------------ + +The sheath boundaries are often implemented in the physics model. +Previously of they where implemented using a `RangeIterator`:: + + class yboundary_example_legacy { + public: + yboundary_example_legacy(Options* opt, const Field3D& N, const Field3D& V) + : N(N), V(V) { + Options& options = *opt; + lower_y = options["lower_y"].doc("Boundary on lower y?").withDefault(lower_y); + upper_y = options["upper_y"].doc("Boundary on upper y?").withDefault(upper_y); + } + + void rhs() { + BoutReal totalFlux = 0; + if (lower_y) { + for (RangeIterator r = mesh->iterateBndryLowerY(); !r.isDone(); r++) { + for (int jz = 0; jz < mesh->LocalNz; jz++) { + // Calculate flux through surface [normalised m^-2 s^-1], + // should be positive since V < 0.0 + BoutReal flux = + -0.5 * (N(r.ind, mesh->ystart, jz) + N(r.ind, mesh->ystart - 1, jz)) * 0.5 + * (V(r.ind, mesh->ystart, jz) + V(r.ind, mesh->ystart - 1, jz)); + totalFlux += flux; + } + } + } + if (upper_y) { + for (RangeIterator r = mesh->iterateBndryUpperY(); !r.isDone(); r++) { + for (int jz = 0; jz < mesh->LocalNz; jz++) { + // Calculate flux through surface [normalised m^-2 s^-1], + // should be positive since V < 0.0 + BoutReal flux = -0.5 * (N(r.ind, mesh->yend, jz) + N(r.ind, mesh->yend + 1, jz)) + * 0.5 + * (V(r.ind, mesh->yend, jz) + V(r.ind, mesh->yend + 1, jz)); + totalFlux += flux; + } + } + } + } + + private: + bool lower_y{true}; + bool upper_y{true}; + const Field3D& N; + const Field3D& V; + } + + +This can be replaced using the `YBoundary` class, which not only simplifies the +code, but also allows to have the same code working with non-field-aligned +geometries, as flux coordinate independent (FCI) method:: + + #include + + class yboundary_example { + public: + yboundary_example(Options* opt, const Field3D& N, const Field3D& V) : + N(N), V(V) {} + + void rhs() { + BoutReal totalFlux = 0; + mesh->getCoordinates()->getYBoundary()->iter_points([&](auto& point) { + BoutReal flux = point.interpolate_sheath_o2(N) * point.interpolate_sheath_o2(V); + totalFlux += flux; + }); + } + + private: + const Field3D& N; + const Field3D& V; + }; + + + +There are several member functions of ``point``. ``point`` is of type +`BoundaryRegionParIterBase` and `BoundaryRegionIter`, and both should provide +the same interface. If they don't that is a bug, as the above code is a +template, that gets instantiated for both types, and thus requires both +classes to provide the same interface, one for FCI-like boundaries and one for +field aligned boundaries. + +Here is a short summary of some members of ``point``, where ``f`` is a : + +.. list-table:: Members for boundary operation + :widths: 15 70 + :header-rows: 1 + + * - Function + - Description + * - ``point.ythis(f)`` + - Returns the value at the last point in the domain + * - ``point.ynext(f)`` + - Returns the value at the first point in the boundary, i.e. one beyond the domain. + * - ``point.yprev(f)`` + - Returns the value at the second to last point in the domain, if it is + valid. NB: this point may not be valid. + * - ``point.interpolate_sheath_o2(f)`` + - Returns the value at the boundary, assuming the bounday value has been set + * - ``point.extrapolate_sheath_o1(f)`` + - Returns the value at the boundary, extrapolating from the bulk, first order + * - ``point.extrapolate_sheath_o2(f)`` + - Returns the value at the boundary, extrapolating from the bulk, second order + * - ``point.extrapolate_next_o{1,2}(f)`` + - Extrapolate into the boundary from the bulk, first or second order + * - ``point.extrapolate_grad_o{1,2}(f)`` + - Extrapolate the gradient into the boundary, first or second order + * - ``point.dirichlet_o{1,2,3}(f, v)`` + - Apply dirichlet boundary conditions with value ``v`` and given order + * - ``point.neumann_o{1,2,3}(f, v)`` + - Applies a gradient of ``v / dy`` boundary condition. + * - ``point.limitFree(f)`` + - Extrapolate into the boundary using only monotonic decreasing values. + ``f`` needs to be positive. + * - ``point.dir()`` + - The direction of the boundary. + + + + Boundary regions ---------------- @@ -573,7 +697,7 @@ and implemented in ``boundary_standard.cxx`` void BoundaryNeumann::apply(Field3D &f) { for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) + for(int z= mesh->zstart; z <= mesh->zend;z++) f[bndry->x][bndry->y][z] = f[bndry->x - bndry->bx][bndry->y - bndry->by][z]; } diff --git a/manual/sphinx/user_docs/bout_options.rst b/manual/sphinx/user_docs/bout_options.rst index f63e65b6df..eb71de26e3 100644 --- a/manual/sphinx/user_docs/bout_options.rst +++ b/manual/sphinx/user_docs/bout_options.rst @@ -100,7 +100,7 @@ A number of functions are defined, listed in table is that if a number comes before a symbol or an opening bracket (``(``) then a multiplication is assumed: ``2x+3y^2`` is the same as ``2*x + 3*y^2``, which with the usual precedence rules is the same as -``(2*x) + (3*(y^2))``. +``(2*x) + (3*(y^2))``. Expressions can span more than one line, which can make long expressions easier to read: @@ -176,7 +176,7 @@ Special symbols in Option names If option names start with numbers or ``.`` or contain symbols such as ``+`` and ``-`` then these symbols need to be escaped in expressions or they will be treated as arithmetic operators like addition or -subtraction. To escape a single character +subtraction. To escape a single character ``\`` (backslash) can be used, for example ``plasma\-density * 10`` would read the option ``plasma-density`` and multiply it by 10 e.g @@ -247,13 +247,13 @@ combinations of the format codes:: // [section1] // value1 = 42 - // value2 = hello # doc: This says hello + // value2 = hello # doc: This says hello // // [section2] // value5 = 3 // // [section2:subsection1] - // value3 = true # type: bool, doc: This is a bool + // value3 = true # type: bool, doc: This is a bool // value4 = 3.2 // Only keys, inline sections, and 'doc', 'type', and 'source' attributes. @@ -261,9 +261,9 @@ combinations of the format codes:: output.write("{:kids}", options); // section1:value1 - // section1:value2 # doc: This says hello + // section1:value2 # doc: This says hello // section2:value5 - // section2:subsection1:value3 # type: bool, doc: This is a bool, source: a test + // section2:subsection1:value3 # type: bool, doc: This is a bool, source: a test // section2:subsection1:value4 @@ -554,42 +554,42 @@ may be useful anyway. See :ref:`sec-output` for more details. Input and Output ---------------- -The output (dump) files with time-history are controlled by settings -in a section called “output”. Restart files contain a single -time-slice, and are controlled by a section called “restart”. The -options available are listed in table :numref:`tab-outputopts`. +The output (dump) files with time-history are controlled by settings in a +section called ``"output"``. Restart files contain a single time-slice, and are +controlled by a section called ``"restart"``. The options available are listed +in table :numref:`tab-outputopts`. .. _tab-outputopts: .. table:: Output file options - - +-------------+----------------------------------------------------+--------------+ - | Option | Description | Default | - | | | value | - +-------------+----------------------------------------------------+--------------+ - | enabled | Writing is enabled | true | - +-------------+----------------------------------------------------+--------------+ - | type | File type e.g. "netcdf" or "adios" | "netcdf" | - +-------------+----------------------------------------------------+--------------+ - | prefix | File name prefix | "BOUT.dmp" | - +-------------+----------------------------------------------------+--------------+ - | path | Directory to write the file into | ``datadir`` | - +-------------+----------------------------------------------------+--------------+ - | floats | Write floats rather than doubles | false | - +-------------+----------------------------------------------------+--------------+ - | flush | Flush the file to disk after each write | true | - +-------------+----------------------------------------------------+--------------+ - | guards | Output guard cells | true | - +-------------+----------------------------------------------------+--------------+ - | openclose | Re-open the file for each write, and close after | true | - +-------------+----------------------------------------------------+--------------+ -| + +----------------------+-----------------------------------------+----------------+ + | Option | Description | Default value | + +======================+=========================================+================+ + | ``append`` | Append to existing file if true, | ``false`` | + | | otherwise overwrite | | + +----------------------+-----------------------------------------+----------------+ + | ``enabled`` | Writing is enabled | ``true`` | + +----------------------+-----------------------------------------+----------------+ + | ``flush_frequency`` | How many output timesteps between | ``1`` | + | | writing output to disk (NetCDF only) | | + +----------------------+-----------------------------------------+----------------+ + | ``prefix`` | File name prefix | ``"BOUT.dmp"`` | + +----------------------+-----------------------------------------+----------------+ + | ``path`` | Directory to write the file into | ``datadir`` | + +----------------------+-----------------------------------------+----------------+ + | ``type`` | File type, either ``"netcdf"`` or | ``"netcdf"`` | + | | ``"adios"`` | | + +----------------------+-----------------------------------------+----------------+ -**enabled** is useful mainly for doing performance or scaling tests, where you -want to exclude I/O from the timings. **floats** can be used to reduce the size -of the output files: files are stored as double by default, but setting -**floats = true** changes the output to single-precision floats. +| +- ``enabled`` is useful mainly for doing performance or scaling tests, where you + want to exclude I/O from the timings. +- If you find that IO is taking more and more time as your simulation goes on, + try setting ``flush_frequency`` to a larger value such as ``10``. This can + workaround an issue with NetCDF where subsequent writes take longer and + longer. However, larger values risk losing more data in the event of a crash + or the simulation being killed early. Implementation -------------- @@ -597,7 +597,7 @@ Implementation To control the behaviour of BOUT++ a set of options is used, with options organised into sections which can be nested. To represent this tree structure there is the `Options` class defined in -``bout++/include/options.hxx``. +``bout++/include/options.hxx``. To access the options, there is a static function (singleton):: @@ -610,7 +610,7 @@ assigning, treating options as a map or dictionary:: options["nout"] = 10; // Integer options["restart"] = true; // bool - + Internally these values are stored in a variant type, which supports commonly used types including strings, integers, real numbers and fields (2D and 3D). Since strings can be stored, any type can be assigned, so long as it can be @@ -783,7 +783,7 @@ currently supported, but use of the newer interface above is encouraged. To access the options, there is a static function (singleton):: - + Options *options = Options::getRoot(); which gives the top-level (root) options class. Setting options is done @@ -889,7 +889,7 @@ Fields can also be stored and written:: Options fields; fields["f2d"] = Field2D(1.0); fields["f3d"] = Field3D(2.0); - bout::OptionsIO::create("fields.nc").write(fields); + bout::OptionsIO::create("fields.nc")->write(fields); This allows the input settings and evolving variables to be combined into a single tree (see above on joining trees) and written @@ -909,10 +909,10 @@ Note that by default reading as ``Field2D`` or ``Field3D`` will use the global pass a field which the result should be similar to:: Field3D example = ... // Some existing field - + Field3D f3d = fields_in["f3d"].as(example); -Meta data like ``Mesh`` pointer, will be taken from ``example``. +Meta data like ``Mesh`` pointer, will be taken from ``example``. Currently converting from ``Matrix`` or ``Tensor`` types only works if the data in the ``Matrix`` or ``Tensor`` is the same size as the @@ -938,19 +938,19 @@ automatically set the ``"time_dimension"`` attribute:: data["scalar"] = 1.0; // You can set the attribute manually like so: data["scalar"].attributes["time_dimension"] = "t"; - + // Or use `assignRepeat` to do it automatically: data["field"].assignRepeat(Field3D(2.0)); - + bout::OptionsIO::create("time.nc")->write(data); - + // Update time-dependent values. This can be done without `force` if the time_dimension // attribute is set data["scalar"] = 2.0; data["field"] = Field3D(3.0); - + // Append data to file - bout::OptionsIO({{"file", "time.nc"}, {"append", true}})->write(data); + bout::OptionsIO::create({{"file", "time.nc"}, {"append", true}})->write(data); .. note:: By default, `bout::OptionsIO::write` will only write variables with a ``"time_dimension"`` of ``"t"``. You can write diff --git a/manual/sphinx/user_docs/differential_operators.rst b/manual/sphinx/user_docs/differential_operators.rst index 25abc15e70..eb497a03db 100644 --- a/manual/sphinx/user_docs/differential_operators.rst +++ b/manual/sphinx/user_docs/differential_operators.rst @@ -36,7 +36,7 @@ exceptions are possible) and are divided into three categories: :math:`(-f_{-2} + 16f_{-1} - 30f_0 + 16f_1 - f_2)/12` - ``S2``: 2\ :math:`^{nd}` order smoothing derivative - + - ``W2``: 2\ :math:`^{nd}` order CWENO - ``W3``: 3\ :math:`^{rd}` order CWENO @@ -46,9 +46,9 @@ exceptions are possible) and are divided into three categories: - ``U1``: 1\ :math:`^{st}` order upwinding - ``U2``: 2\ :math:`^{nd}` order upwinding - + - ``U3``: 3\ :math:`^{rd}` order upwinding - + - ``U4``: 4\ :math:`^{th}` order upwinding - ``C2``: 2\ :math:`^{nd}` order central @@ -75,7 +75,7 @@ Special methods : - ``SPLIT``: A flux method that splits into upwind and central terms :math:`\frac{d}{dx}(v_x f) = v_x\frac{df}{dx} + f\frac{dv_x}{dx}` - + .. _Weighted Essentially Non-Oscillatory (WENO): https://doi.org/10.1137/S106482759732455X @@ -178,7 +178,7 @@ implemented. return 0.5*(f.p - f.m); }; - + Here `DEFINE_STANARD_DERIV` is a macro that acts on the kernel ``return 0.5*(f.p - f.m);`` and produces the functor that will apply the differencing method over an entire field. The macro takes several @@ -667,6 +667,18 @@ values. Several slope limiters are defined in ``fv_ops.hxx``: to ``MinMod``. It has smaller dissipation than ``MinMod`` so is the default. +* ``VanAlbada`` - A smooth (differentiable) symmetric slope limiter which + avoids piecewise branches at extrema. This can be useful for nonlinear + solvers and finite-difference Jacobian calculations. + +* ``WENO3`` - A third-order smooth WENO (Jiang-Shu) cell-face reconstruction + using a three-point stencil. This is typically less dissipative than TVD + slope limiters, but is not strictly monotonicity-preserving. + +Useful resources on slope limiters include: + +* `Wikipedia's Flux Limiter page `_ +* `SU2's Slope Limiters and Shock Resolution page `_ .. _sec-staggeredgrids: diff --git a/manual/sphinx/user_docs/field_expressions.rst b/manual/sphinx/user_docs/field_expressions.rst new file mode 100644 index 0000000000..62a50988d6 --- /dev/null +++ b/manual/sphinx/user_docs/field_expressions.rst @@ -0,0 +1,151 @@ +.. _sec-field-expressions: + +Field Expressions +================= + +BOUT++ field algebra now supports *lazy expressions* for many common +operations. Instead of creating a temporary field for every ``+``, +``-``, ``*``, ``/``, ``sqrt`` or ``abs``, BOUT++ can keep the expression +symbolic and evaluate it only when a concrete field or scalar result is +needed. + +This keeps ordinary model code readable while reducing temporary +allocations and extra loops over the mesh. It is especially helpful for +accelerator backends, where launching fewer kernels matters. + +What stays lazy +--------------- + +The following operations can form lazy expressions over `Field2D`, +`Field3D`, `Field3DParallel`, and `FieldPerp` where the combination makes +sense: + +- Arithmetic operators: ``+``, ``-``, ``*``, ``/`` +- Unary algebraic operators such as ``sqrt``, ``abs``, ``exp``, ``log``, + ``sin``, ``cos``, ``tan``, ``sinh``, ``cosh``, ``tanh``, ``floor``, + and ``SQ`` +- Simple conditionals with ``if_else`` and ``if_else_zero`` +- Reductions such as ``min``, ``max``, and ``mean`` + +For example:: + + Field3D n, T; + Field3D result; + + result = sqrt(SQ(n) + SQ(T)); + +The right-hand side can stay lazy until the assignment to ``result``. + +When evaluation happens +----------------------- + +An expression is evaluated when BOUT++ needs actual storage or a scalar +answer. Common triggers are: + +- assigning to a field +- constructing a field from an expression +- assigning a field expression into an `Options` object +- calling scalar reductions such as ``min``, ``max``, or ``mean`` + +Examples:: + + Field3D result = n + T; + options["rhs"] = n + T; + BoutReal max_value = max(abs(n + T), true); + +Region-limited expressions +-------------------------- + +Many algebraic operators take a ``region`` argument, usually defaulting +to ``RGN_ALL``. A lazy expression keeps track of that region. + +Only values inside the requested region are guaranteed to be valid after +materialization. This is useful for skipping guard-cell work when the +result will only be used in a smaller region:: + + Field3D interior = abs(n, "RGN_NOBNDRY"); + +As with other region-limited field operations in BOUT++, code that later +uses guard cells should communicate or otherwise fill those cells before +relying on them. + +Metadata propagation +-------------------- + +When an expression is materialized into a field, BOUT++ propagates the +field metadata carried by the expression: + +- mesh pointer +- cell location +- field directions +- for `FieldPerp`, the y-index + +This means expressions are intended to behave like ordinary field +operations in user code. Compatibility checks still apply: combining +fields on different meshes or incompatible staggered locations is an +error. + +Mixed field types +----------------- + +Several mixed-type combinations are supported directly: + +- `Field2D` with `Field3D`: the 2D quantity is broadcast in ``z`` +- `FieldPerp` with matching perpendicular data: the operation uses the + `FieldPerp` y-index +- expressions involving metric components may return + `Coordinates::FieldMetric`, which is `Field2D` or `Field3D` depending + on how BOUT++ was built + +In practice, this means code such as:: + + Coordinates::FieldMetric grad = coords->J / coords->g_22; + Field3D rhs = density * temperature + background_2d; + +can use the same algebraic style even when metric dimensionality or +field rank differs. + +Conditionals +------------ + +``if_else`` selects between two algebraic branches without forcing the +branches to be precomputed:: + + Field3D rhs = if_else(use_source, source * density, sink * density); + +``if_else_zero(condition, expr)`` is a shorthand for selecting either an +expression or zero:: + + Field3D rhs = if_else_zero(include_drive, drive * profile); + +This is particularly convenient when optional source terms are enabled +or disabled by compile-time or run-time logic. + +Reductions on expressions +------------------------- + +Reductions can operate directly on expressions instead of requiring an +intermediate field:: + + BoutReal rms = sqrt(mean(SQ(n - n0), true, "RGN_NOBNDRY")); + BoutReal max_error = max(abs(lhs - rhs), true); + +This is often clearer than explicitly constructing a temporary field, +and it avoids extra storage. + +Relation to GPU execution +------------------------- + +Lazy field expressions are the high-level path to reducing temporary +work. They are a good default when ordinary field algebra expresses the +operation clearly. + +For more control, especially when you want to fuse derivative operators +into a single explicit loop, see :ref:`sec-gpusupport`. + +See also +-------- + +- :doc:`algebraic_operators` +- :doc:`gpu_support` +- :doc:`differential_operators` diff --git a/manual/sphinx/user_docs/gpu_support.rst b/manual/sphinx/user_docs/gpu_support.rst index cc0cba8def..670c1a9b32 100644 --- a/manual/sphinx/user_docs/gpu_support.rst +++ b/manual/sphinx/user_docs/gpu_support.rst @@ -3,68 +3,92 @@ GPU support =========== -This section describes work in progress to develop GPU support in -BOUT++ models. It includes both configuration and compilation on GPU -systems, but also ways to write physics models which are designed to -give higher performance. These methods may also be beneficial for CPU -architectures, but have fewer safety checks, less functionality and -run-time flexibility than the field operators. +This section describes the main ways to run BOUT++ work efficiently on +GPUs or other accelerator-style backends. -To use the single index operators and the ``BOUT_FOR_RAJA`` loop macro:: +There are now two complementary levels of optimization: + +1. Write ordinary field algebra and let BOUT++ keep many algebraic + expressions lazy until assignment or reduction. +2. Drop down to explicit `RAJA` loops and single-index operators when + you want complete control over loop fusion and kernel structure. + +The first approach is usually the best starting point. The second is for +hot loops where you want to manually combine derivative operators, +accessors, and run-time captures in one kernel. + +Automatic fusion with field expressions +--------------------------------------- + +Many algebraic operations on fields can now be represented as lazy +expressions. This keeps user code close to the familiar field-based +style while reducing temporary fields and extra passes over memory. + +Typical examples are: + +.. code-block:: cpp + + Field3D rhs = sqrt(SQ(n) + SQ(T)); + ddt(n) = source * profile - sink * n; + BoutReal max_error = max(abs(lhs - rhs), true); + +This is the highest-level route to better execution behavior, and it is +usually the most maintainable. See :ref:`sec-field-expressions` for the +details of what stays lazy and when evaluation happens. + +Lazy expressions mainly help with *algebraic* fusion. If your hot path +is dominated by differential operators and you need to fuse those +operators into a single explicit loop, use the lower-level approach +described below. + +Manual fusion with RAJA loops +----------------------------- + +To use the single-index operators and the ``BOUT_FOR_RAJA`` loop macro:: #include "bout/single_index_ops.hxx" #include "bout/rajalib.hxx" -To run parts of a physics model RHS function on a GPU, the basic -outline of the code is to (optionally) first copy any class member -variables which will be used in the loop into local variables -(see below for an alternative method):: +To run part of a physics-model RHS on a GPU, start by copying any class +member variables needed inside the loop into local variables, or capture +them explicitly:: - auto _setting = setting; // Create a local variable to capture + auto _setting = setting; -Then create a `FieldAccessor` to efficiently access field and -coordinate system data inside the loop:: +Then create `FieldAccessor` objects to read and write field data inside +the loop:: auto n_acc = FieldAccessor<>(n); auto phi_acc = FieldAccessor<>(phi); -There are also ``Field2DAccessor``s for accessing ``Field2D`` -types. If fields are staggered, then the expected location should be -passed as a template parameter:: +There are also ``Field2DAccessor`` objects for `Field2D`. If fields are +staggered, the expected location can be supplied as a template +parameter:: auto Jpar_acc = FieldAccessor(Jpar); -which enables the cell location to be checked in the operators at -compile time rather than run time. +Finally the loop itself can be written as:: -Finally the loop itself can be written something like:: + Field3D result; + auto result_acc = FieldAccessor<>(result); BOUT_FOR_RAJA(i, region) { - ddt(n_acc)[i] = -bracket(phi_acc, n_acc, i) - 2 * DDZ(n_acc, i); - /* ... */ + result_acc[i] = -bracket(phi_acc, n_acc, i) - 2.0 * DDZ(n_acc, i); }; Note the semicolon after the closing brace, which is needed because -this is the body of a lambda function. Inside the body of the loop, -the operators like ``bracket`` and ``DDZ`` calculate the derivatives -at a single index ``i``. These are "single index operators` and are -defined in ``bout/single_index_ops.hxx``. - -Any class member variables which are used inside the loop must be captured -as a local variable. If this is not done, then the code will probably compile, -but may produce an illegal memory access error at runtime on the GPU. To -capture the class member, you can copy any class member variables which -will be used in the loop into local variables:: +this is the body of a lambda function. Inside the loop, operators such +as ``bracket`` and ``DDZ`` act at a single index ``i``. These are the +single-index operators defined in ``bout/single_index_ops.hxx``. - auto _setting = setting; // Create a local variable to capture - -and then use ``_setting`` rather than ``setting`` inside the loop. -Alternatively, add variables to be captured to a CAPTURE argument to -the ``BOUT_FOR_RAJA`` loop:: +Any class member variables used inside the loop must be captured +carefully. Otherwise the code may compile but fail at run time on the +GPU. Instead of using ``this`` implicitly, either shadow members with +local variables or add them to the capture list:: BOUT_FOR_RAJA(i, region, CAPTURE(setting)) { ddt(n_acc)[i] = -bracket(phi_acc, n_acc, i) - 2 * DDZ(n_acc, i); - /* ... code which uses `setting` ... */ + /* ... code that uses `setting` ... */ }; If RAJA is not available, the ``BOUT_FOR_RAJA`` macro will revert to @@ -75,10 +99,26 @@ Note: An important difference between ``BOUT_FOR`` and ``BOUT_FOR_RAJA`` (apart from the closing semicolon) is that the type of the index ``i`` is different inside the loop: ``BOUT_FOR`` uses ``SpecificInd`` types (typically ``Ind3D``), but ``BOUT_FOR_RAJA`` -uses ``int``. ``SpecificInd`` can be explicitly cast to ``int`` so +uses ``int``. ``SpecificInd`` can be explicitly cast to ``int`` so use ``static_cast(i)`` to ensure that it's an integer both with and without RAJA. This might (hopefully) change in future versions. +Choosing between the two approaches +----------------------------------- + +Use lazy field expressions when: + +- the code is mostly algebraic combinations of existing fields +- readability matters more than extracting the last bit of performance +- you want a clear default path that still maps well to accelerator + backends + +Use explicit RAJA loops and single-index operators when: + +- a hot loop is dominated by derivatives +- you want to combine many operations into one kernel manually +- you need direct control over captures, data access, or loop structure + Examples -------- @@ -115,8 +155,12 @@ Notes: CMake configuration ------------------- -To compile BOUT++ components into GPU kernels a few different pieces need to be configured to work together: -RAJA, Umpire, and a CUDA compiler. +To compile BOUT++ components into GPU kernels, a few different pieces +need to work together: RAJA, Umpire, and a CUDA-capable compiler. + +The generated eager field-operator code also selects a loop backend at +configure time. If RAJA is enabled it uses RAJA loops, otherwise it +falls back to OpenMP or serial loops depending on the build. .. _tab-gpusupport-cmake: @@ -136,6 +180,25 @@ RAJA, Umpire, and a CUDA compiler. | BOUT_ENABLE_WARNINGS | nvcc has incompatible warning flags | On (turn Off for CUDA) | +----------------------+-----------------------------------------+------------------------+ +Shifted metric on GPUs +---------------------- + +When BOUT++ is built with CUDA, the shifted-metric parallel transform +has a CUDA implementation of its toroidal ``shiftZ`` work used while +calculating parallel slices during communication. + +This is most relevant when using: + +.. code-block:: cfg + + [mesh:paralleltransform] + type = shifted + calcParallelSlices_on_communicate = true + +The current implementation is specialized for supported power-of-two +``LocalNz`` values. If parallel slices are disabled on communicate, as in +the aligned-transform workflow, this precomputed-slice path is not used. + Single index operators ---------------------- @@ -263,7 +326,7 @@ likely that the results might be architecture dependent. To minimise the number of times this data needs to be copied from individual fields into the single array, and then copied from CPU to -GPU, ``CoordinatesAccessor``s are cached. A map (``coords_store`` +GPU, ``CoordinatesAccessor``\ s are cached. A map (``coords_store`` defined in ``coordinates_accessor.cxx``) associates ``Array`` objects (containing the array of data) to ``Coordinates`` pointers. If a ``CoordinatesAccessor`` is constructed @@ -314,10 +377,10 @@ This is a `good talk by John Lakos [ACCU 2017] on memory allocators Future work ----------- -Indices -~~~~~~~ - -Setting up a RAJA loop to run on a GPU is still cumbersome and inefficient +The GPU path is still evolving. The main long-term direction is to let +more of ordinary field code map efficiently onto accelerator backends, +so that manual kernel construction is only needed for the most +performance-critical cases. due to the need to transform CPU data structures into a form which can be passed to and used on the GPU. In the ``bout/rajalib.hxx`` header there is code like:: @@ -332,7 +395,7 @@ is code like:: auto _ob_i_ind_raw = &_ob_i_ind[0]; which is creating a raw pointer (``_ob_i_ind_raw``) to an array of -``int``s which are allocated using Umpire. The original ``indices`` +``int``\ s which are allocated using Umpire. The original ``indices`` are allocated using ``new`` and are inside a C++ ``std::vector``. The RAJA loop then uses this array like this:: diff --git a/manual/sphinx/user_docs/input_grids.rst b/manual/sphinx/user_docs/input_grids.rst index 3d6be2cf77..402fb30d06 100644 --- a/manual/sphinx/user_docs/input_grids.rst +++ b/manual/sphinx/user_docs/input_grids.rst @@ -155,237 +155,27 @@ The only quantities which are required are the sizes of the grid. If these are the only quantities specified, then the coordinates revert to Cartesian. -This section describes how to generate inputs for tokamak equilibria. If -you’re not interested in tokamaks then you can skip to the next section. - -The directory ``tokamak_grids`` contains code to generate input grid -files for tokamaks. These can be used by, for example, the ``2fluid`` and -``highbeta_reduced`` modules. - -.. _sec-bout-topology: - -BOUT++ Topology ---------------- - -Basic -~~~~~ - -BOUT++ is designed to work in a variety of tokamak and non-tokamak -geometries, from simple slabs to disconnected double-null -configurations. In order to handle tokamak geometry BOUT++ contains an -internal topology which is built from six regions determined by four -branch-cut locations and two separatrix locations (``ixseps1`` and -``ixseps2``). There are some limitations on these regions that we will -discuss below, and some regions may be empty, all of which enables -BOUT++ to describe effectively seven types of topology: - -- "core": this type of topology can describe the closed field line - regions inside the separatrix of tokamaks or other devices, or - idealised geometries like periodic slabs; - -- "SOL": these can describe the open field line regions of the - scrape-off layer (SOL) outside the separatrix of a tokamak, or linear - devices with a target plate at either end; - -- "limiter": these topologies have an open field line region and a - region where field lines hit a boundary, without an X-point; - -- "X-point": these topologies have four separate legs with their own - boundaries, and no closed field line region; - -- "single null": this type of topology has one X-point with two separate - legs, closed and an open field line regions, and a single separatrix; - -- "connected double null": these topologies have two X-points with two - separate legs each, closed and open field line regions and a single - separatrix that connects both X-points; - -- "disconnected double null": finally, these are similar to connected - double null geometries except that they have two separatrices that do - not connect the two X-points. These come in "lower" and "upper" - flavours, depending on which X-point is adjacent to the closed field - line region. - -The six regions that form the building blocks of these topologies are: - -- four separate "leg" regions that have a boundary in the ``y`` - direction; - -- two "core" regions that do not have boundaries in ``y``. - -Each of these regions may have additional boundaries in the ``x`` -direction. The separate regions are illustrated in -:numref:`fig-topology-cross-section`: the grey dashed lines show the -region partitions, with the sections labelled 1, 2, and 3 forming one -leg; 4, 5, and 6 forming one core region, and so on. The internal names -for these separate regions use "inner" and "outer" in reference to the -major radius -- that is, "inner" regions correspond to the left-hand -side of :numref:`fig-topology-cross-section` and "outer" regions to the -right-hand side. - -Two important limitations for BOUT++ grids are that a single processor -can only belong to one region, and that there must be the same number of -points on each processor. The first limitation means that certain -topologies require a minimum number of processors. For example, a -disconnected double null configuration uses all six regions -- therefore -the minimum number of processors able to describe this in BOUT++ is -six. Having equal numbers of points on each processor can put some -restrictions on the resolution of simulations. - -The two separatrix locations are ``ixseps1`` and ``ixseps2``, these are -the global indices in the ``x`` domain where the first and second -separatrices are located. These values are set either in the grid file -or in ``BOUT.inp``. :numref:`fig-topology-cross-section` shows -schematically how ``ixseps`` is used. - -If ``ixseps1 == ixseps2`` then there is a single separatrix representing -the boundary between the core region and the SOL region and the grid is -a connected double null configuration. If ``ixseps1 > ixseps2`` then -there are two separatrices and the inner separatrix is ``ixseps2`` so -the tokamak is an upper double null. If ``ixseps1 < ixseps2`` then there -are two separatrices and the inner separatrix is ``ixseps1`` so the -tokamak is a lower double null. - -In other words: Let us for illustrative purposes say that ``ixseps1 > -ixseps2`` (see :numref:`fig-topology-cross-section`). Let us say that we -have a field ``f(x,y,z)`` with a global ``x``-index which includes ghost -points. ``f(x <= ixseps1, y, z)``) will then be periodic in the -``y``-direction, ``f(ixspes1 < x <= ixseps2, y, z)``) will have boundary -condition in the ``y``-direction set by the lowermost ``ydown`` and -``yup``. If ``f(ixspes2 < x, y, z)``) the boundary condition in the -``y``-direction will be set by the uppermost ``ydown`` and ``yup``. As -for now, there is no difference between the two sets of upper and lower -``ydown`` and ``yup`` boundary conditions (unless manually specified, -see :ref:`sec-custom-BC`). - -The four branch cut locations, ``jyseps1_1``, ``jyseps1_2``, -``jyseps2_1``, and ``jyseps2_2``, split the ``y`` domain into logical -regions defining the SOL, the PFR (private flux region) and the core of -the tokamak. This is illustrated also in -:numref:`fig-topology-cross-section`. If ``jyseps1_2 == jyseps2_1`` then -the grid is a single null configuration, otherwise the grid is a double -null configuration. - -.. _fig-topology-cross-section: -.. figure:: ../figs/topology_cross_section.* - :alt: Cross-section of the tokamak topology used in BOUT++ - - Deconstruction of a poloidal tokamak cross-section into logical - domains using the parameters ``ixseps1``, ``ixseps2``, - ``jyseps1_1``, ``jyseps1_2``, ``jyseps2_1``, and ``jyseps2_2``. This - configuration is a "disconnected double null" and shows all the - possible regions used in the BOUT++ topology. - -Advanced -~~~~~~~~ - -The internal domain in BOUT++ is deconstructed into a series of -logically rectangular sub-domains with boundaries determined by the -``ixseps`` and ``jyseps`` parameters. The boundaries coincide with -processor boundaries so the number of grid points within each sub-domain -must be an integer multiple of ``ny/nypes`` where ``ny`` is the number -of grid points in ``y`` and ``nypes`` is the number of processors used -to split the y domain. Processor communication across the domain -boundaries is then handled internally. :numref:`fig-topology-schematic` -shows schematically how the different regions of a double null tokamak -with ``ixseps1 = ixseps2`` are connected together via communications. - -.. note:: - To ensure that each subdomain follows logically, the - ``jyseps`` indices must adhere to the following conditions: - - - ``jyseps1_1 > -1`` - - ``jyseps2_1 >= jyseps1_1 + 1`` - - ``jyseps1_2 >= jyseps2_1`` - - ``jyseps2_2 >= jyseps1_2`` - - ``jyseps2_2 <= ny - 1`` - - To ensure that communications work branch cuts must align with - processor boundaries. - -.. _fig-topology-schematic: -.. figure:: ../figs/topology_schematic.* - - Schematic illustration of domain decomposition and communication in - BOUT++ with ``ixseps1 = ixseps2`` - -Periodic X domains -~~~~~~~~~~~~~~~~~~ - -The :math:`x` coordinate is usually a radial flux coordinate. In some -simulations it is useful to make this direction periodic, for example -flux tube simulations or the Hasegawa-Wakatani example in -``examples/hasegawa-wakatani/hw.cxx``. In that example the :math:`x` -coordinate is made periodic with the top-level ``periodicX`` option: +You can read additional quantities from the grid and make them available in +expressions in the input file by listing them in the ``input:grid_variables`` +section, with the key being the name in the grid file (``mesh:file``) and the +value being the type (one of ``field3d``, ``field2d``, ``boutreal``): .. code-block:: cfg - periodicX = true # Domain is periodic in X + [input:grid_variables] + rho = field2d + theta = field2d + scale = boutreal [mesh] + B = (scale / rho) * cos(theta) - nx = 260 # Note 4 guard cells in X - ny = 1 - nz = 256 # Periodic, so no guard cells in Z - -Note that some care is now needed if the model uses Laplacian -inversions, for example to calculate electrostatic potential from -vorticity: If both :math:`x` and :math:`z` coordinates are both -periodic then the inversion has no boundary conditions. In that case -the laplacian has a null space and so is singular; an arbitrary -constant offset can be added to the potential without changing the -vorticity. - -The default ``cyclic`` solver treats the :math:`k_z = -0` (DC) mode as a special case, setting the average of the potential -over the :math:`x-z` domain to zero. Other solvers may not -handle the ``periodicX`` case in the same way. - -Implementations -~~~~~~~~~~~~~~~ - -In BOUT++ each processor has a logically rectangular domain, so any -branch cuts needed for X-point geometry (see -:numref:`fig-topology-schematic`) must be at processor boundaries. - -In the standard “bout” mesh (``src/mesh/impls/bout/``), the -communication is controlled by the variables - -.. code-block:: cpp - - int UDATA_INDEST, UDATA_OUTDEST, UDATA_XSPLIT; - int DDATA_INDEST, DDATA_OUTDEST, DDATA_XSPLIT; - int IDATA_DEST, ODATA_DEST; - -These control the behavior of the communications as shown in -:numref:`fig-boutmesh-comms`. - -.. _fig-boutmesh-comms: -.. figure:: ../figs/boutmesh-comms.* - :alt: Communication of guard cells in BOUT++ - - Communication of guard cells in BOUT++. Boundaries in X have only - one neighbour each, but boundaries in Y can be split into two, - allowing branch cuts - -In the Y direction, each boundary region (**U**\ p and **D**\ own in Y) -can be split into two, with ``0 <= x < UDATA_XSPLIT`` going to the -processor index ``UDATA_INDEST``, and ``UDATA_INDEST <= x < LocalNx`` going -to ``UDATA_OUTDEST``. Similarly for the Down boundary. Since there are -no branch-cuts in the X direction, there is just one destination for the -**I**\ nner and **O**\ uter boundaries. In all cases a negative -processor number means that there’s a domain boundary so no -communication is needed. - -The communication control variables are set in the -`BoutMesh::topology()` function, in -``src/mesh/impls/bout/boutmesh.cxx``. First the function -`default_connections()` sets the topology to be a rectangle. +This section describes how to generate inputs for tokamak equilibria. If +you’re not interested in tokamaks then you can skip to the next section. -To change the topology, the function `BoutMesh::set_connection` checks -that the requested branch cut is on a processor boundary, and changes -the communications consistently so that communications are two-way and -there are no “dangling” communications. +The directory ``tokamak_grids`` contains code to generate input grid +files for tokamaks. These can be used by, for example, the ``2fluid`` and +``highbeta_reduced`` modules. 3D variables ------------ @@ -431,6 +221,12 @@ has been developed to create BOUT++ input files from R-Z equilibria. This can re (geqdsk) files, find flux surfaces, and calculate metric coefficients. +From GRIDUE files +-------------- + +A separate tool (in python) called `INGRID `_ has been modified to be able to output BOUT++ input files too. It can read EFIT ’g’ (geqdsk) files, find flux surfaces, and calculate metric coefficients + to create grids for complex topologies such as the snowflake configurations. + From ELITE and GATO files ------------------------- diff --git a/manual/sphinx/user_docs/installing.rst b/manual/sphinx/user_docs/installing.rst index 05db86c277..ead172a442 100644 --- a/manual/sphinx/user_docs/installing.rst +++ b/manual/sphinx/user_docs/installing.rst @@ -332,10 +332,13 @@ The default build configuration options try to be sensible for new users and developers, but there are a few you probably want to set manually for production runs or for debugging: -* ``CMAKE_BUILD_TYPE``: The default is ``RelWithDebInfo``, which - builds an optimised executable with debug symbols included. Change - this to ``Release`` to remove the debug symbols, or ``Debug`` for an - unoptimised build, but better debug experience +* ``CMAKE_BUILD_TYPE``: The default is ``RelWithDebInfo``, which builds an + optimised executable with debug symbols included. This is generally the most + useful, except for developers, who may wish to use ``Debug`` for an + unoptimised build, but better debug experience. There are a couple of other + choices (``Release`` and ``MinSizeRel``) which also produce optimised + executables, but without debug symbols, which is only really useful for + producing smaller binaries. * ``CHECK``: This sets the level of internal runtime checking done in the BOUT++ library, and ranges from 0 to 4 (inclusive). By default, @@ -380,16 +383,32 @@ to read and write this high-performance parallel file format. Bundled Dependencies ~~~~~~~~~~~~~~~~~~~~ -BOUT++ bundles some dependencies, currently `mpark.variant -`_, `fmt `_ and -`googletest `_. If you wish to -use an existing installation of ``mpark.variant``, you can set -``-DBOUT_USE_SYSTEM_MPARK_VARIANT=ON``, and supply the installation -path using ``mpark_variant_ROOT`` via the command line or environment -variable if it is installed in a non standard loction. Similarly for -``fmt``, using ``-DBOUT_USE_SYSTEM_FMT=ON`` and ``fmt_ROOT`` -respectively. To turn off both, you can set -``-DBOUT_USE_GIT_SUBMODULE=OFF``. +BOUT++ bundles some dependencies, currently: + +- `mpark.variant `_ +- `fmt `_ +- `cpptrace `_ +- ``googletest `_ (for unit tests) + +Aside from ``googletest``, the others are required dependencies and can either +be built as part of the BOUT++ build, or provided externally. If you wish to use +existing installations of some of these, set the following flags: + ++--------------------+-----------------------------------+------------------------+ +| Name | Flag for external installation | Library path | ++====================+===================================+========================+ +| ``mpark.variant`` | ``BOUT_USE_SYSTEM_MPARK_VARIANT`` | ``mpark_variant_ROOT`` | ++--------------------+-----------------------------------+------------------------+ +| ``fmt`` | ``BOUT_USE_SYSTEM_FMT`` | ``fmt_ROOT`` | ++--------------------+-----------------------------------+------------------------+ +| ``cpptrace`` | ``BOUT_USE_SYSTEM_CPPTRACE`` | ``cpptrace_ROOT`` | ++--------------------+-----------------------------------+------------------------+ + +You can also set ``-DBOUT_USE_GIT_SUBMODULE=OFF`` to not use any of the bundled +versions. + +If the libraries are in non-standard locations, you may also need to supply the +relevant library path flags. The recommended way to use ``googletest`` is to compile it at the same time as your project, therefore there is no option to use an external @@ -482,7 +501,7 @@ or conda:: $ conda install numpy scipy matplotlib sympy netcdf4 future importlib-metadata -They may also be available from your Linux system's package manager. +They may also be available from your Linux system's package manager. For example on Fedora:: diff --git a/manual/sphinx/user_docs/invertable_operator.rst b/manual/sphinx/user_docs/invertable_operator.rst index 92b7efd288..8f92796c9c 100644 --- a/manual/sphinx/user_docs/invertable_operator.rst +++ b/manual/sphinx/user_docs/invertable_operator.rst @@ -48,7 +48,6 @@ the ``operator()`` call:: // Drop C term for now Field3D operator()(const Field3D &input) { - TRACE("myLaplacian::operator()"); Timer timer("invertable_operator_operate"); Field3D result = A * input + D * Delp2(input); @@ -68,7 +67,6 @@ A more complete example is :: // Drop C term for now Field3D operator()(const Field3D &input) { - TRACE("myLaplacian::operator()"); Timer timer("invertable_operator_operate"); Field3D result = A * input + D * Delp2(input); diff --git a/manual/sphinx/user_docs/laplacian.rst b/manual/sphinx/user_docs/laplacian.rst index 1529149726..ade760d5c5 100644 --- a/manual/sphinx/user_docs/laplacian.rst +++ b/manual/sphinx/user_docs/laplacian.rst @@ -847,7 +847,7 @@ subdomain using the Thomas algorithm. **Constraints.** This method requires that: -* ``NXPE`` is a power of 2. +* ``NXPE`` is a power of 2. * ``NXPE > 2^(max_levels+1)`` .. _sec-LaplaceXY: diff --git a/manual/sphinx/user_docs/parallel-transforms.rst b/manual/sphinx/user_docs/parallel-transforms.rst index 3ee3eccfb8..9d9b94af9e 100644 --- a/manual/sphinx/user_docs/parallel-transforms.rst +++ b/manual/sphinx/user_docs/parallel-transforms.rst @@ -120,6 +120,21 @@ Note that here :math:`\theta_0` does not need to be constant in X (radius), since it is only the relative shifts between Y locations which matters. +When BOUT++ is built with CUDA, the shifted-metric implementation also +has a GPU path for the ``shiftZ`` work used to calculate parallel +slices during communication. This is most useful in the standard +shifted-metric workflow with + +.. code-block:: cfg + + [mesh:paralleltransform] + type = shifted + calcParallelSlices_on_communicate = true + +If ``calcParallelSlices_on_communicate = false`` is used, BOUT++ is in +the aligned-transform mode described below, so those precomputed +parallel slices are not generated on communicate. + Special handling is needed for parallel boundary conditions, see :ref:`sec-parallel-bc-shifted-metric`. diff --git a/manual/sphinx/user_docs/physics_models.rst b/manual/sphinx/user_docs/physics_models.rst index d9a45f4014..25aefdddd3 100644 --- a/manual/sphinx/user_docs/physics_models.rst +++ b/manual/sphinx/user_docs/physics_models.rst @@ -528,7 +528,7 @@ is the macro:: OPTION(options, g, 5.0/3.0); which is equivalent to:: - + g = options["g"].withDefault( 5.0/3.0 ); See :ref:`sec-options` for more details of how to use the input @@ -734,14 +734,14 @@ and ``a(1,y,z)`` is in the domain, then the boundary would be set so that:: rearranged as:: a(0,y,z) = - a(1,y,z) + b(0,y,z) + b(1,y,z) - + To copy the boundary cells (and communication guard cells), iterate over them:: BOUT_FOR(i, a.getRegion("RGN_GUARDS")) { a[i] = b[i]; } - + See :ref:`sec-iterating` for more details on iterating over custom regions. .. _sec-custom-bc: diff --git a/manual/sphinx/user_docs/python_boutpp.rst b/manual/sphinx/user_docs/python_boutpp.rst index 939a817ffe..e79facbf55 100644 --- a/manual/sphinx/user_docs/python_boutpp.rst +++ b/manual/sphinx/user_docs/python_boutpp.rst @@ -160,7 +160,7 @@ A real example - check derivative contributions: phi[-1,:,z]=phi_arr with open(path+"/equilibrium/phi_eq.dat","rb") as inf: phi_arr=np.fromfile(inf,dtype=np.double) - bm="BRACKET_ARAKAWA_OLD" + bm="BRACKET_ARAKAWA" for tind in range(len(times)): vort = Field3D.fromCollect("vort" ,path=path,tind=tind,info=False) diff --git a/manual/sphinx/user_docs/running_bout.rst b/manual/sphinx/user_docs/running_bout.rst index 5ee7a9bfb3..f0c53fc74e 100644 --- a/manual/sphinx/user_docs/running_bout.rst +++ b/manual/sphinx/user_docs/running_bout.rst @@ -673,4 +673,3 @@ then the BOUT++ restart will fail. **Note** It is a good idea to set ``nxpe`` in the ``BOUT.inp`` file to be consistent with what you set here. If it is inconsistent then the restart will fail, but the error message may not be particularly enlightening. - diff --git a/manual/sphinx/user_docs/supported_topologies.rst b/manual/sphinx/user_docs/supported_topologies.rst new file mode 100644 index 0000000000..0bb2aa50cd --- /dev/null +++ b/manual/sphinx/user_docs/supported_topologies.rst @@ -0,0 +1,201 @@ +.. _sec-supported-topologies: + +Supported Topologies +==================== + +BOUT++ can handle any tokamak topology with up to two X-points. Currently, the INGRID +and Hypnotoad gridding tools are the tools used to create BOUT++ grids. +Hypnotoad can create grids for any of the **basic and common topologies** while +INGRID can create grids for any of the **common and complex topologies**. + +.. note:: + An EQDSK file is needed to create a grid in either of these tools. + +Basic Topologies +---------------- + +Basic topologies are used for simpler simulations and don't need to use the +branch cut features. These include: + +- **"Closed Flux Surface" (CFL)**: Which encompass the **"core"** and + **"SOL"** configurations. +- **"Open Flux Surface" (OFL)**: Which encompass the **"limiter"**, + **"X-point"**, and **"slab"** configurations. + +These don't require a minimum number of processors to run. + +Common Topologies +----------------- + +Single Null (SN) +~~~~~~~~~~~~~~~~ + +The most common topology; most tokamaks operate or are able to use this +configuration. + +.. figure:: ../figs/Topologies/SN/SN_Geometry.* + :alt: The geometry of a single null configuration. + + Single null geometry on the RZ plane generated by INGRID. + +The regions that form the building blocks of this topology are: + +- 2 "leg" regions which have a boundary in the ``y`` direction; +- 1 "core" region which does not have boundaries in ``y``. + +.. figure:: ../figs/Topologies/SN/SN_processors.* + :alt: The processor layout of a single null configuration. + + Division of Single Null configuration into processor layout in index space to compare with the INGRID geometry diagram. + +.. figure:: ../figs/Topologies/SN/SN_regions.* + :alt: The regions of a single null configuration. + + Single null configuration regions defined by the branch cuts in :math:`y`. + + +Even though BOUT++ does not requiere each region to be divided into more processors, using an INGRID grid enables this division and makes it easier to understand the topology. + +.. figure:: ../figs/Topologies/SN/SN_topology.* + :alt: The topology of a single null configuration. + + Single null configuration topology diagram derived from the processor layout. + +Connected Double Null (CDN) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The introduction of a secondary divertor on the top of a tokamak is what +characterises this configuration. It has a secondary X-point associated with +the secondary divertor but the separatrix connects both X-points +(``ixseps1 == ixseps2``). It is an unstable configuration and quite hard to +maintain experimentally for a long period of time. + +.. figure:: ../figs/Topologies/CDN/CDN_Geometry.* + :alt: The geometry of a connected double null configuration. + + Connected double null geometry on the RZ plane generated by INGRID. + +.. figure:: ../figs/Topologies/CDN/CDN_processors.* + :alt: The processor layout of a connected double null configuration. + + Division of Connected Double Null configuration into processor layout in index space to compare with the INGRID geometry diagram. + +.. figure:: ../figs/Topologies/CDN/CDN_regions.* + :alt: The regions of a connected double null configuration. + + Connected double null configuration regions defined by the branch cuts in :math:`y`. + +.. figure:: ../figs/Topologies/CDN/CDN_topology.* + :alt: The topology of a connected double null configuration. + + Connected double null configuration topology diagram derived from the processor layout. + +Unconnected Double Null (UDN) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This more realistic configuration has a secondary X-point associated with the +secondary divertor but the X-points are not connected, creating a secondary +separatrix (``ixseps1 != ixseps2``). Depending on which separatrix coincides +with the start of the inner SOL, it is either an upper or lower **UDN**. +MAST-U is the most famous example of a reactor using the Double Null +configurations. + +.. figure:: ../figs/Topologies/UDN/UDN_Geometry.* + :alt: The geometry of an unconnected double null configuration. + + Unconnected double null geometry on the RZ plane generated by INGRID. + + +.. figure:: ../figs/Topologies/UDN/UDN_processors.* + :alt: The processor layout of an unconnected double null configuration. + + Division of Unconnected Double Null configuration into processor layout in index space to compare with the INGRID geometry diagram. + +.. figure:: ../figs/Topologies/UDN/UDN_regions.* + :alt: The regions of an unconnected double null configuration. + + Unconnected double null configuration regions defined by the branch cuts in :math:`y`. + +.. figure:: ../figs/Topologies/UDN/UDN_topology.* + :alt: The topology of an unconnected double null configuration. + + Unconnected double null configuration topology diagram derived from the processor layout. + +Both **DN configurations** have 2 upper targets defined by ``ny_inner`` and +``ny_inner + 1``. The regions that form the building blocks of these topologies +are: + +- 4 "leg" regions which have a boundary in the ``y`` direction; +- 2 "core" regions which do not have boundaries in ``y``. + +Complex Topologies +------------------ + +Snowflake (SF) +~~~~~~~~~~~~~~ + +The snowflake features a secondary X-point close to the primary one. This +creates a second-order poloidal field null that branches the separatrix into +4 legs instead of 2. Much like the **CDN**, the ideal **SF** is very hard to +maintain experimentally, so SF configurations branch into either **SF+** or **SF-**. + +Snowflake+ (SF+) +~~~~~~~~~~~~~~~~ + +The **SF+** configuration has the secondary X-point in the PFR . Unlike the perfect **SF**, +this features an extra central PFR. + +.. figure:: ../figs/Topologies/SF/SFplus_Geometry.* + :alt: The geometry of a snowflake configuration. + + Snowflake geometry on the RZ plane generated by INGRID. + +Snowflake- (SF-) +~~~~~~~~~~~~~~~~ + +The **SF-** configuration has the secondary X-point in the scrape-off layer as opposed to the private flux region. In the case of an "exact" **SF-**, both X-Points would be at different points on the same flux surface. +Usually, **SF-** configurations have worst heat and particle distributions than the rest of the SF configurations. + +.. figure:: ../figs/Topologies/SF/SFminus_Geometry.png + :alt: The geometry of a snowflake configuration. + + Snowflake geometry on the RZ plane generated by INGRID. + +X-Point Target (XPT) +~~~~~~~~~~~~~~~~~~~~ + +The **X-Point Target** configuration has the main separatrix extended a longer +distance, the secondary X-point happens far away from the core plasma, and there is no PFR between the East and South East targets. +This configuration is especially interesting for studying the effect of detachment and neutral gas injection on the plasma without contaminating the core. + +.. todo:: + + Add geometry diagram. + +All **SF and X-Point Target configurations** have 4 targets defined by: + +- West on ``y = 0`` +- East on ``y = ny_inner`` +- South East on ``y = ny_inner + 1`` +- South West on ``y =`` :math:`2\pi` + +Topologically, all the **SF configurations** and the **X-Point Target** are +the same. The regions that form the building blocks of these topologies are: + +- 4 "leg" regions which have a boundary in the ``y`` direction; +- 1 "core" region which does not have boundaries in ``y``. + +.. figure:: ../figs/Topologies/SF/SF_processors.* + :alt: The processor layout of a snowflake configuration. + + Division of Snowflake configuration into processor layout in index space to compare with the INGRID geometry diagram. + +.. figure:: ../figs/Topologies/SF/SF_regions.* + :alt: The regions of a snowflake configuration. + + Snowflake configuration regions defined by the branch cuts in :math:`y`. + +.. figure:: ../figs/Topologies/SF/SF_topology.* + :alt: The topology of a snowflake configuration. + + Snowflake configuration topology diagram derived from the processor layout. diff --git a/manual/sphinx/user_docs/time_integration.rst b/manual/sphinx/user_docs/time_integration.rst index 5a33b85e08..f05c11a9ae 100644 --- a/manual/sphinx/user_docs/time_integration.rst +++ b/manual/sphinx/user_docs/time_integration.rst @@ -33,7 +33,7 @@ needed to make the solver available. .. _tab-solvers: .. table:: Available time integration solvers - + +---------------+-----------------------------------------+------------------------+ | Name | Description | Compile options | +===============+=========================================+========================+ @@ -68,7 +68,7 @@ given in table :numref:`tab-solveropts`. .. _tab-solveropts: .. table:: Time integration solver options - + +--------------------------+--------------------------------------------+-------------------------------------+ | Option | Description | Solvers used | +==========================+============================================+=====================================+ @@ -87,7 +87,10 @@ given in table :numref:`tab-solveropts`. +--------------------------+--------------------------------------------+-------------------------------------+ | adaptive | Adapt timestep? (Y/N) | rk4, imexbdf2 | +--------------------------+--------------------------------------------+-------------------------------------+ - | use\_precon | Use a preconditioner? (Y/N) | pvode, cvode, ida, imexbdf2 | + | use\_precon | Use a preconditioner? (Y/N) | pvode, ida, imexbdf2 | + +--------------------------+--------------------------------------------+-------------------------------------+ + | cvode\_precon\_method | CVODE preconditioner: none, auto, user, | cvode | + | | petsc, or bbd | | +--------------------------+--------------------------------------------+-------------------------------------+ | mudq, mldq | BBD preconditioner settings | pvode, cvode, ida | +--------------------------+--------------------------------------------+-------------------------------------+ @@ -104,6 +107,9 @@ given in table :numref:`tab-solveropts`. +--------------------------+--------------------------------------------+-------------------------------------+ | diagnose | Collect and print additional diagnostics | cvode, imexbdf2, beuler | +--------------------------+--------------------------------------------+-------------------------------------+ + | nvector | ``N_Vector`` backend for SUNDIALS solvers: | cvode, ida, arkode | + | | ``sundials`` or ``manyvector`` | | + +--------------------------+--------------------------------------------+-------------------------------------+ | @@ -111,6 +117,30 @@ The most commonly changed options are the absolute and relative solver tolerances, ``atol`` and ``rtol`` which should be varied to check convergence. +SUNDIALS ``N_Vector`` backends +------------------------------ + +The SUNDIALS-based solvers ``cvode``, ``ida``, and ``arkode`` can select +the ``N_Vector`` backend at runtime using ``solver:nvector``: + +.. code-block:: cfg + + [solver] + type = cvode + nvector = sundials + +Valid values are: + +- ``sundials`` uses the standard SUNDIALS parallel ``N_Vector``. This is the + default. +- ``manyvector`` uses the BOUT++ field-backed custom ``N_Vector`` built on top + of SUNDIALS ManyVector support. + +The ``manyvector`` option is only available when BOUT++ was built with SUNDIALS +ManyVector support. If ``solver:nvector=manyvector`` is selected in a build +that does not provide this support, solver initialisation will throw an +exception. + CVODE ----- @@ -144,6 +174,31 @@ iterations becomes large, this may be an indication that the system is poorly conditioned, and a preconditioner might help improve performance. See :ref:`sec-preconditioning`. +CVODE preconditioning is controlled using ``solver:cvode_precon_method``: + +- ``none`` (default): Disable preconditioning. +- ``auto``: Prefer a user-supplied preconditioner if provided, then PETSc + coloring if PETSc is available, otherwise use BBD. +- ``user``: Require a user-supplied preconditioner. +- ``petsc``: Require PETSc and use PETSc coloring. +- ``bbd``: Force the built-in BBD preconditioner. + +For ``cvode_precon_method = petsc``, PETSc options for the internal KSP/PC can be +set with the prefix ``cvode_petscpre_`` (either on the command line, or by putting +prefixed keys into the ``[petsc]`` section). For example:: + + [petsc] + cvode_petscpre_ksp_type = preonly + cvode_petscpre_pc_type = hypre + +Two CVODE heuristics that control when the linear solver setup routine is called, +and when the Jacobian/preconditioner are recomputed, can be adjusted with: + +- ``cvode_lsetup_frequency`` (default ``0``): Passed to ``CVodeSetLSetupFrequency``. + ``0`` uses the SUNDIALS default. +- ``cvode_jac_eval_frequency`` (default ``0``): Passed to ``CVodeSetJacEvalFrequency``. + ``0`` uses the SUNDIALS default. + CVODE can set constraints to keep some quantities positive, non-negative, negative or non-positive. These constraints can be activated by setting the option ``solver:apply_positivity_constraints=true``, and then in the section @@ -165,6 +220,9 @@ nonlinear solvers: `CVodeSetEpsLin `_. +The linear solver type can be set using the ``linear_solver`` option. +Valid choices include ``gmres`` (the default), ``fgmres``, ``tfqmr``, ``bcgs``. + IMEX-BDF2 --------- @@ -358,16 +416,426 @@ And the adaptive timestepping options: Backward Euler - SNES --------------------- -The `beuler` or `snes` solver type (either name can be used) is -intended mainly for solving steady-state problems, so integrates in -time using a stable but low accuracy method (Backward Euler). It uses -PETSc's SNES solvers to solve the nonlinear system at each timestep, -and adjusts the internal timestep to keep the number of SNES -iterations within a given range. +The `beuler` or `snes` solver type (either name can be used) is a PETSc-based implicit +solver for finding steady-state solutions to systems of partial differential equations. +It supports multiple solution strategies including backward Euler timestepping, +direct Newton iteration, and Pseudo-Transient Continuation (PTC) with Switched +Evolution Relaxation (SER). + +Basic Configuration +~~~~~~~~~~~~~~~~~~~ + +The SNES solver is configured through the ``[solver]`` section of the input file: + +.. code-block:: ini + + [solver] + type = snes + + # Nonlinear solver settings + snes_type = newtonls # anderson, newtonls, newtontr, nrichardson + atol = 1e-7 # Absolute tolerance + rtol = 1e-6 # Relative tolerance + stol = 1e-12 # Solution change tolerance + max_nonlinear_iterations = 20 # Maximum SNES iterations per solve + + # Linear solver settings + ksp_type = fgmres # Linear solver: gmres, bicgstab, etc. + maxl = 20 # Maximum linear iterations + pc_type = ilu # Preconditioner: ilu, bjacobi, hypre, etc. + +Timestepping Modes +~~~~~~~~~~~~~~~~~~ + +The solver supports several timestepping strategies controlled by ``equation_form``: + +**Backward Euler (default)** + Standard implicit backward Euler method. Good for general timestepping. + + .. code-block:: ini + + equation_form = rearranged_backward_euler # Default + + This method has low accuracy in time but its dissipative properties + are helpful when evolving to steady state solutions. + +**Direct Newton** + Solves the steady-state problem F(u) = 0 directly without timestepping. + + .. code-block:: ini + + equation_form = direct_newton + + This method is unlikely to converge unless the system is very close + to steady state. + +**Pseudo-Transient Continuation** + Uses pseudo-time to guide the solution to steady state. Recommended for + highly nonlinear problems where Newton's method fails. + + .. code-block:: ini + + equation_form = pseudo_transient + + This uses the same form as rearranged_backward_euler, but the time step + can be different for each cell. + +Adaptive Timestepping +~~~~~~~~~~~~~~~~~~~~~ + +When ``equation_form = rearranged_backward_euler`` (default), the +solver uses global timestepping with adaptive timestep control based +on nonlinear iteration count. + +.. code-block:: ini + + [solver] + type = snes + equation_form = rearranged_backward_euler + + # Initial and maximum timesteps + timestep = 1.0 # Initial timestep + max_timestep = 1e10 # Upper limit on timestep + dt_min_reset = 1e-6 # Reset the solver when timestep < this + + # Timestep adaptation + timestep_control = pid_nonlinear_its + target_its = 7 # Target number of nonlinear iterations + kP = 0.7 # Proportional gain + kI = 0.3 # Integral gain + kD = 0.2 # Derivative gain + +This uses a PID controller that adjusts the timestep to maintain approximately ``target_its`` +nonlinear iterations per solve. + +Residual Ratio +^^^^^^^^^^^^^^ + +This adjusts the timestep using the ratio of global residuals and a timestep factor: + +.. math:: + + dt_n = r dt_{n-1} \frac{||F(X_{n-1})||}{||F(X_{n})|| + +so that as the residual falls the timestep :math:`dt` is increased. +The :math:`r` parameter is input option ``timestep_factor`` that has +default value 1.1. + +.. code-block:: ini + + [solver] + timestep_control = residual_ratio # Use global residual + timestep_factor = 1.1 # Constant timestep factor + +Threshold Controller +^^^^^^^^^^^^^^^^^^^^ + +An alternative adaptive strategy uses thresholds in nonlinear iterations +to adjust the timestep: + +.. code-block:: ini + + [solver] + timestep_control = threshold_nonlinear_its + lower_its = 3 # Increase dt if iterations < this + upper_its = 10 # Decrease dt if iterations > this + timestep_factor_on_lower_its = 1.4 # Growth factor + timestep_factor_on_upper_its = 0.9 # Reduction factor + timestep_factor_on_failure = 0.5 # Reduction on convergence failure + +The adjustments are less smooth than the default PID method, but the +timestep is changed less frequently. This may enable the Jacobian and +preconditioner to be used for more iterations. + +Output trigger +~~~~~~~~~~~~~~ + +The default behavior is to save outputs at a regular time interval, as +BOUT++ solvers do. This is desirable when performing time-dependent +simulations, but for simulations that are trying to get to steady +state a better measure of progress is reduction of the global residual +(norm of the time-derivatives of the system). + +.. code-block:: ini + + [solver] + output_trigger = residual_ratio # Trigger an output based on the ratio of residuals + output_residual_ratio = 0.5 # Output when global residual is multiplied by this + +With this choice, each output has a global residual that is less than +or equal to `output_residual_ratio` times the last output global +residual. This provides a way of measuring progress to steady state +that is independent of time integration accuracy. + +Pseudo-Transient Continuation and Switched Evolution Relaxation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When ``equation_form = pseudo_transient`` the solver uses +Pseudo-Transient Continuation (PTC). This is a robust numerical +technique for solving steady-state problems that are too nonlinear for +direct Newton iteration. Instead of solving the steady-state system +**F(u) = 0** directly, PTC solves a modified time-dependent problem: + +.. math:: + + M(u) \frac{\partial u}{\partial \tau} + F(u) = 0 + +where :math:`\tau` is a pseudo-time variable (not physical time) and :math:`M(u)` +is a preconditioning matrix. As :math:`\tau \to \infty`, the solution converges +to the steady state **F(u) = 0**. + +The key advantage of PTC is that it transforms a difficult root-finding problem +into a sequence of easier initial value problems. Poor initial guesses that would +cause Newton's method to diverge can still reach the solution via a stable +pseudo-transient path. + +The Switched Evolution Relaxation (SER) method is a spatially adaptive +variant of PTC that allows each cell to use a different +pseudo-timestep :math:`\Delta\tau_i`. The timestep in each cell adapts +based on the local residual, allowing the algorithm to take large +timesteps in well-behaved regions (fast convergence), while taking +small timesteps in difficult regions (stable advancement). The the +same :math:`\Delta\tau_i` is used for all equations (density, +momentum, energy etc.) within each cell. This maintains coupling +between temperature, pressure, and composition through the equation of +state. + +**Key parameters:** + +``pseudo_max_ratio`` (default: 2.0) + Maximum allowed ratio of timesteps between neighboring cells. This prevents + sharp spatial gradients in convergence rate. + +**Example PTC configuration:** + +.. code-block:: ini + + [solver] + type = snes + equation_form = pseudo_transient + + timestep = 1.0 # Initial timestep + + # SER parameters + timestep_control = pid_nonlinear_its # Scale timesteps based on iterations + pseudo_max_ratio = 2.0 # Limit neighbor timestep ratio + + # Tolerances + atol = 1e-7 + rtol = 1e-6 + stol = 1e-12 + +SER timestep strategy +^^^^^^^^^^^^^^^^^^^^^ + +After each nonlinear solve the timesteps in each cell are adjusted. +The strategy used depends on the ``pseudo_strategy`` option: + +**inverse_residual** (default) + +If ``pseudo_strategy = inverse_residual`` then the timestep is inversely +proportional to the RMS residual in each cell. +``pseudo_alpha`` (default: 100 × atol × timestep) +Controls the relationship between residual and timestep. The local timestep +is computed as: + +.. math:: + + \Delta\tau_i = \frac{\alpha}{||R_i||} + +Larger values allow more aggressive timestepping. The default is to use +a fixed ``pseudo_alpha`` but a better strategy is to enable the PID controller +that adjusts this parameter based on the nonlinear solver convergence. + +The timestep is limited to be between ``dt_min_reset`` and +``max_timestep``. In addition the timestep is limited between 0.67 × +previous timestep and 1.5 × previous timestep, to limit sudden changes +in timestep. + +In practice this strategy seems to work well, though problems could +arise when residuals become very small. + +**history_based** + +When ``pseudo_strategy = history_based`` the history of residuals +within each cell is used to adjust the timestep. The key parameters +are: + +``pseudo_growth_factor`` (default: 1.1) + Factor by which timestep increases when residual decreases successfully. + +``pseudo_reduction_factor`` (default: 0.5) + Factor by which timestep decreases when residual increases (step rejected). + +This method may be less susceptible to fluctuations when residuals +become small, but tends to be slower to converge when residuals are +large. + +**hybrid** + +When ``pseudo_strategy = hybrid`` the ``inverse_residual`` and +``history_based`` strategies are combined: When the residuals are +large the ``inverse_residual`` method is used, and when residuals +become small the method switches to ``history_based``. + +PID Controller +^^^^^^^^^^^^^^ + +When using the PTC method the PID controller can be used to dynamically +adjust ``pseudo_alpha`` depending on the nonlinearity of the system: + +.. code-block:: ini + + [solver] + timestep_control = pid_nonlinear_its # Scale global timestep using PID controller + target_its = 7 # Target number of nonlinear iterations + kP = 0.7 # Proportional gain + kI = 0.3 # Integral gain + kD = 0.2 # Derivative gain + +The PID controller adjusts ``pseudo_alpha``, scaling all cell +timesteps together, to maintain approximately ``target_its`` nonlinear +iterations per solve. + +With this enabled the solver uses the number of nonlinear iterations +to scale timesteps globally, and residuals to scale timesteps locally. +Note that the PID controller has no effect on the ``history_based`` +strategy because that strategy does not use ``pseudo_alpha``. + +Jacobian Finite Difference with Coloring +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default and recommended approach for most problems: + +.. code-block:: ini + + [solver] + use_coloring = true # Enable (default) + lag_jacobian = 5 # Reuse Jacobian for this many iterations + + # Stencil shape (determines Jacobian sparsity pattern) + stencil:taxi = 2 # Taxi-cab distance (default) + stencil:square = 0 # Square stencil extent + stencil:cross = 0 # Cross stencil extent + +The coloring algorithm exploits the sparse structure of the Jacobian to reduce +the number of function evaluations needed for finite differencing. + +Jacobian coloring stencil +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The stencil used to create the Jacobian colouring can be varied, +depending on which numerical operators are in use. It is important to +note that the coloring won't work for every problem: It assumes that +each evolving quantity is coupled to all other evolving quantities on +the same grid cell, and on all the neighbouring grid cells. If the RHS +function includes Fourier transforms, or matrix inversions +(e.g. potential solves) then these will introduce longer-range +coupling and the Jacobian calculation will give spurious +results. Generally the method will then fail to converge. Two +solutions are to a) switch to matrix-free (``matrix_free=true``), +or b) solve the matrix inversion as a constraint. + + +``solver:stencil:cross = N`` +e.g. for N == 2 + +.. code-block:: bash + + * + * + * * x * * + * + * + + +``solver:stencil:square = N`` +e.g. for N == 2 + +.. code-block:: bash + + * * * * * + * * * * * + * * x * * + * * * * * + * * * * * + +``solver:stencil:taxi = N`` +e.g. for N == 2 + +.. code-block:: bash + + * + * * * + * * x * * + * * * + * + +Setting ``solver:force_symmetric_coloring = true``, will make sure +that the jacobian colouring matrix is symmetric. This will often +include a few extra non-zeros that the stencil will miss otherwise + + +Variable Scaling +~~~~~~~~~~~~~~~~ + +There may be differences of many orders of magnitude between your +variables or within variables across the domain. This can result in a +particular area of the domain for a particular variable dominating the +residual in the nonlinear solve because its residual has the largest +absolute value, even if not the largest relative value. As a +consequence, tighter tolerances will be needed to ensure other +variables and parts of the domain are solved to sufficient +accuracy. The ``scale_vars`` option can help address this by +renormalising all variables to be of order unity across the entire +domain. + +.. code-block:: ini + + scale_vars = true + rescale_period = 30 # Maximum number of time-steps taken before rescaling the variables + rescale_threshold = 100. # Approximate overall change to variables permitted before rescaling + +It has been found that scaling variables in this way allows +simulations to run with much looser tolerances than would otherwise be +possible (e.g., ``rtol = 1e-5`` and ``atol = 1e-3``). Four-times +speedups have been observed by doing this. Once a steady-state has +been reached the simulation can be run for a further few time-steps +with tighter tolerances to improve the accuracy. + +Diagnostics and Monitoring +--------------------------- + +.. code-block:: ini + + [solver] + diagnose = true # Print iteration info to screen + diagnose_failures = true # Detailed diagnostics on failures + +When ``equation_form = pseudo_transient``, the solver saves additional diagnostic fields: + +- ``snes_pseudo_residual``: Local residual in each cell +- ``snes_pseudo_timestep``: Local pseudo-timestep in each cell +- ``snes_pseudo_alpha``: Global timestep scaling + +These can be visualized to understand convergence behavior and identify +problematic regions. + +The residuals from the last nonlinear solve are also saved with names +``resid_``. Plotting these can help to understand which +variables and parts of the domain are controlling convergence. + +Summary of solver options +~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------+---------------+----------------------------------------------------+ | Option | Default |Description | +===========================+===============+====================================================+ +| pseudo_time | false | Pseudo-Transient Continuation (PTC) method, using | +| | | a different timestep for each cell. | ++---------------------------+---------------+----------------------------------------------------+ +| pseudo_max_ratio | 2. | Maximum timestep ratio between neighboring cells | ++---------------------------+---------------+----------------------------------------------------+ | snes_type | newtonls | PETSc SNES nonlinear solver (try anderson, qn) | +---------------------------+---------------+----------------------------------------------------+ | ksp_type | gmres | PETSc KSP linear solver | @@ -393,7 +861,9 @@ iterations within a given range. +---------------------------+---------------+----------------------------------------------------+ | predictor | true | Use linear predictor? | +---------------------------+---------------+----------------------------------------------------+ -| matrix_free | false | Use matrix free Jacobian-vector product? | +| matrix_free | false | Matrix-free preconditioning? | ++---------------------------+---------------+----------------------------------------------------+ +| matrix_free_operator | false | Use matrix free Jacobian-vector product? | +---------------------------+---------------+----------------------------------------------------+ | use_coloring | true | If ``matrix_free=false``, use coloring to speed up | | | | calculation of the Jacobian elements. | @@ -402,11 +872,18 @@ iterations within a given range. +---------------------------+---------------+----------------------------------------------------+ | kspsetinitialguessnonzero | false | If true, Use previous solution as KSP initial | +---------------------------+---------------+----------------------------------------------------+ -| use_precon | false | Use user-supplied preconditioner? | +| use_precon | false | If ``matrix_free=true``, use user-supplied | +| | | preconditioner? | | | | If false, the default PETSc preconditioner is used | +---------------------------+---------------+----------------------------------------------------+ | diagnose | false | Print diagnostic information every iteration | +---------------------------+---------------+----------------------------------------------------+ +| stencil:cross | 0 | If ``matrix_free=false`` and ``use_coloring=true`` | +| stencil:square | 0 | Set the size and shape of the Jacobian coloring | +| stencil:taxi | 2 | stencil. | ++---------------------------+---------------+----------------------------------------------------+ +| force_symmetric_coloring | false | Ensure that the Jacobian coloring is symmetric | ++---------------------------+---------------+----------------------------------------------------+ The predictor is linear extrapolation from the last two timesteps. It seems to be effective, but can be disabled by setting ``predictor = false``. @@ -415,16 +892,7 @@ The default `newtonls` SNES type can be very effective if combined with Jacobian coloring: The coloring enables the Jacobian to be calculated relatively efficiently; once a Jacobian matrix has been calculated, effective preconditioners can be used to speed up -convergence. It is important to note that the coloring assumes a star -stencil and so won't work for every problem: It assumes that each -evolving quantity is coupled to all other evolving quantities on the -same grid cell, and on all the neighbouring grid cells. If the RHS -function includes Fourier transforms, or matrix inversions -(e.g. potential solves) then these will introduce longer-range -coupling and the Jacobian calculation will give spurious -results. Generally the method will then fail to converge. Two -solutions are to a) switch to matrix-free (``matrix_free=true``), or b) -solve the matrix inversion as a constraint. +convergence. The `SNES type `_ @@ -444,6 +912,8 @@ Preconditioner types: Enable with command-line args ``-pc_type hypre -pc_hypre_type euclid -pc_hypre_euclid_levels k`` where ``k`` is the level (1-8 typically). + + ODE integration --------------- @@ -779,7 +1249,7 @@ then in the ``BOUT.inp`` settings file switch on the preconditioner [solver] type = cvode # Need CVODE or PETSc - use_precon = true # Use preconditioner + cvode_precon_method = user # Use user-supplied preconditioner rightprec = false # Use Right preconditioner (default left) Jacobian function @@ -895,7 +1365,9 @@ implement the outputMonitor method of PhysicsModel:: int outputMonitor(BoutReal simtime, int iter, int nout) The first input is the current simulation time, the second is the output -number, and the last is the total number of outputs requested. +number, and the last is the total number of outputs requested. If an initial +dump is written, it is output number ``0``. Solver output steps are numbered +from ``1`` to ``nout``, so ``iter == nout`` indicates the final output. This method is called by a monitor object PhysicsModel::modelMonitor, which writes the restart files at the same time. You can change the frequency at which the monitor is called by calling, in PhysicsModel::init:: @@ -920,7 +1392,9 @@ returns an int:: The first input is the solver object, the second is the current simulation time, the third is the output number, and the last is the -total number of outputs requested. To get the solver to call this +total number of outputs requested. As for ``outputMonitor()``, output number +``0`` is reserved for the initial dump when it is written, and solver output +steps are numbered from ``1`` to ``NOUT``. To get the solver to call this function every output time, define a `MyOutputMonitor` object as a member of your PhysicsModel:: diff --git a/manual/sphinx/user_docs/topology.rst b/manual/sphinx/user_docs/topology.rst new file mode 100644 index 0000000000..f3d2c8e662 --- /dev/null +++ b/manual/sphinx/user_docs/topology.rst @@ -0,0 +1,224 @@ +.. _sec-bout-topology: + +BOUT++ Topology +=============== + +Basic +----- + +BOUT++ is designed to work in a variety of tokamak and non-tokamak +geometries, from simple slabs to Snowflake configurations. In order to handle +tokamak geometry BOUT++ contains an internal topology which is built from +regions determined by four branch-cut locations (``jyseps*``) and two +separatrix locations (``ixseps1`` and ``ixseps2``). There are some limitations +on these regions that we will discuss below, and some regions may be empty, +all of which enables BOUT++ to describe effectively three types of basic +topologies: + +- **"core"**: this type of topology can describe the closed field line regions + inside the separatrix of tokamaks or other devices, or idealised geometries + like periodic slabs; + +- **"SOL"**: these can describe the open field line regions of the scrape-off + layer (SOL) outside the separatrix of a tokamak, or linear devices with a + target plate at either end; + +- **"limiter"**: these topologies have an open field line region and a region + where field lines hit a boundary, without an X-point; + +- **"X-point"**: these topologies have four separate legs with their own + boundaries, and no closed field line region; + +The "common" topologies: + +- **"single null"**: this type of topology has one X-point with two separate + legs, closed and an open field line regions, and a single separatrix; + +- **"connected double null"**: these topologies have two X-points with two + separate legs each, closed and open field line regions and a single separatrix + that connects both X-points; + +- **"disconnected double null"**: these are similar to connected double null + geometries except that they have two separatrices that do not connect the two + X-points. These come in "lower" and "upper" flavours, depending on which + X-point is adjacent to the closed field line region. + +And all advanced/complex topologies with up to two X-points: + +- **"snowflake"**: The SF topologies feature a second order null point created by two X-points close to each other. The ideal SF has a single separatrix and 4 legs, but more realistic configurations can have an extra PFR between the legs. + The **"snowflake+"** and **"snowflake-"** unlike the perfect **SF**, feature an extra central PFR and the secondary X-point is located either above or below the primary one, respectively (along the y-direction). +- **"X-Point Target"**: The X-Point Target configuration has the main separatrix extended a longer distance and no PFR between the East and South East targets. + +See :ref:`sec-supported-topologies` for more details on the available +topologies. + +The regions that form the building blocks of these topologies are: + +- "leg" regions that have a boundary in the ``y`` direction; +- "core" regions that do not have boundaries in ``y``. + +Each of these regions may have additional boundaries in the ``x`` direction. + +Two important limitations for BOUT++ grids are that a single processor can only +belong to one region, and that there must be the same number of points on each +processor. The first limitation means that certain topologies require a minimum +number of processors. For example, a disconnected double null configuration uses +all six regions — therefore the minimum number of processors able to describe +this in BOUT++ is six. Having equal numbers of points on each processor can put +some restrictions on the resolution of simulations. + +The two separatrix locations are ``ixseps1`` and ``ixseps2``, these are the +global indices in the ``x`` domain where the first and second separatrices are +located. These values are set either in the grid file or in ``BOUT.inp``. + +Considering a Double Null example: + +- If ``ixseps1 == ixseps2`` then there is a single separatrix representing + the boundary between the core region and the SOL region and the grid is a + **connected double null** configuration. +- If ``ixseps1 > ixseps2`` then there are two separatrices and the inner + separatrix is ``ixseps2``, so the tokamak is an **upper double null**. +- If ``ixseps1 < ixseps2`` then there are two separatrices and the inner + separatrix is ``ixseps1``, so the tokamak is a **lower double null**. + +In other words: if ``ixseps1 > ixseps2``, then: + +- ``f(x <= ixseps1, y, z)`` will be periodic in the ``y``-direction (core), +- ``f(ixseps1 < x <= ixseps2, y, z)`` will have boundary condition in ``y`` + set by the lowermost ``ydown`` and ``yup``, +- ``f(ixseps2 < x, y, z)`` will have boundary conditions set by the uppermost + ``ydown`` and ``yup``. + +The four branch cut locations, ``jyseps1_1``, ``jyseps1_2``, ``jyseps2_1``, and +``jyseps2_2``, split the ``y`` domain into logical regions defining the SOL, the +PFRs (private flux regions), and the core of the tokamak. If +``jyseps1_2 == jyseps2_1`` then the grid is a **single null** configuration, +otherwise it can be any of the more advanced configurations. + +.. _fig-topology-cross-section: +.. figure:: ../figs/topology_cross_section.png + :alt: Cross-section of the tokamak topology used in BOUT++ + + Deconstruction of a poloidal tokamak cross-section into logical + domains using the parameters ``ixseps1``, ``ixseps2``, + ``jyseps1_1``, ``jyseps1_2``, ``jyseps2_1``, and ``jyseps2_2``. This + configuration is a "disconnected double null" and shows all the + possible regions used in the BOUT++ topology. + + +Advanced +-------- + +The internal domain in BOUT++ is deconstructed into a series of logically +rectangular sub-domains with boundaries determined by the ``ixseps`` and +``jyseps`` parameters. The boundaries coincide with processor boundaries so the +number of grid points within each sub-domain must be an integer multiple of +``ny/nypes`` where ``ny`` is the number of grid points in ``y`` and ``nypes`` +is the number of processors used to split the y domain. Processor communication +across the domain boundaries is then handled internally. + +.. note:: + To ensure that each subdomain follows logically, the ``jyseps`` indices + must adhere to the following conditions: + + - ``jyseps1_1 > -1`` + - ``jyseps2_1 >= jyseps1_1 + 1`` + - ``jyseps1_2 >= jyseps2_1`` + - ``jyseps2_2 >= jyseps1_2`` + - ``jyseps2_2 <= ny - 1`` + + To ensure that communications work, branch cuts must align with processor + boundaries. + +.. _fig-topology-schematic: +.. figure:: ../figs/topology_schematic.* + + Schematic illustration of domain decomposition and communication in + BOUT++ with ``ixseps1 = ixseps2`` + +These branch cuts are used by the ``getMeshTopology()`` function to determine +which topology is being used. See :ref:`sec-supported-topologies` for a +detailed explanation of the available topologies. + +Number of targets +~~~~~~~~~~~~~~~~~ + +An extra cut in ``y`` called ``ny_inner`` defines where the physical boundary +in the domain is for topologies with more than 2 targets (any topology more +complex than the **single null** needs a "discontinuous" :math:`y` domain). The position of the extra +cut is what distinguishes any **double null** configuration from any of the +**complex** configurations. + +Periodic X Domains +------------------ + +The :math:`x` coordinate is usually a radial flux coordinate. In some +simulations it is useful to make this direction periodic, for example flux tube +simulations or the Hasegawa-Wakatani example in +``examples/hasegawa-wakatani/hw.cxx``. In that example the :math:`x` coordinate +is made periodic with the top-level ``periodicX`` option: + +.. code-block:: cfg + + periodicX = true # Domain is periodic in X + + [mesh] + + nx = 260 # Note 4 guard cells in X + ny = 1 + nz = 256 # Periodic, so no guard cells in Z + +Note that some care is needed if the model uses Laplacian inversions, for +example to calculate electrostatic potential from vorticity: if both :math:`x` +and :math:`z` coordinates are both periodic then the inversion has no boundary +conditions. In that case the Laplacian has a null space and so is singular; an +arbitrary constant offset can be added to the potential without changing the +vorticity. + +The default ``cyclic`` solver treats the :math:`k_z = 0` (DC) mode as a +special case, setting the average of the potential over the :math:`x`-:math:`z` +domain to zero. Other solvers may not handle the ``periodicX`` case in the same +way. + +Implementations +--------------- + +In BOUT++ each processor has a logically rectangular domain, so any branch cuts +needed for X-point geometry must be at processor boundaries. + +In the standard "bout" mesh (``src/mesh/impls/bout/``), the communication is +controlled by the variables + +.. code-block:: cpp + + int UDATA_INDEST, UDATA_OUTDEST, UDATA_XSPLIT; + int DDATA_INDEST, DDATA_OUTDEST, DDATA_XSPLIT; + int IDATA_DEST, ODATA_DEST; + +These control the behavior of the communications as shown in +:numref:`fig-boutmesh-comms`. + +.. _fig-boutmesh-comms: +.. figure:: ../figs/boutmesh-comms.* + :alt: Communication of guard cells in BOUT++ + + Communication of guard cells in BOUT++. Boundaries in X have only + one neighbour each, but boundaries in Y can be split into two, + allowing branch cuts. + +In the Y direction, each boundary region (**U**\ p and **D**\ own in Y) can be +split into two, with ``0 <= x < UDATA_XSPLIT`` going to processor index +``UDATA_INDEST``, and ``UDATA_INDEST <= x < LocalNx`` going to +``UDATA_OUTDEST``. Similarly for the Down boundary. Since there are no +branch-cuts in the X direction, there is just one destination for the +**I**\ nner and **O**\ uter boundaries. In all cases a negative processor +number means that there is a domain boundary so no communication is needed. + +The communication control variables are set in the ``BoutMesh::topology()`` +function, in ``src/mesh/impls/bout/boutmesh.cxx``. First the function +``default_connections()`` sets the topology to be a rectangle. + +To change the topology, the function ``BoutMesh::set_connection`` checks that +the requested branch cut is on a processor boundary, and changes the +communications consistently so that communications are two-way and there are no +"dangling" communications. diff --git a/manual/sphinx/user_docs/variable_init.rst b/manual/sphinx/user_docs/variable_init.rst index ebe989c233..9009ece180 100644 --- a/manual/sphinx/user_docs/variable_init.rst +++ b/manual/sphinx/user_docs/variable_init.rst @@ -83,17 +83,19 @@ following values are also already defined: .. _tab-initexprvals: .. table:: Initialisation expression values - +--------+------------------------------------------------------------------------------------+ - | Name | Description | - +========+====================================================================================+ - | x | :math:`x` position between :math:`0` and :math:`1` | - +--------+------------------------------------------------------------------------------------+ - | y | :math:`y` angle-like position, definition depends on topology of grid | - +--------+------------------------------------------------------------------------------------+ - | z | :math:`z` position between :math:`0` and :math:`2\pi` (excluding the last point) | - +--------+------------------------------------------------------------------------------------+ - | pi π | :math:`3.1415\ldots` | - +--------+------------------------------------------------------------------------------------+ + +----------------+------------------------------------------------------------------------------------+ + | Name | Description | + +================+====================================================================================+ + | x | :math:`x` position between :math:`0` and :math:`1` | + +----------------+------------------------------------------------------------------------------------+ + | y | :math:`y` angle-like position, definition depends on topology of grid | + +----------------+------------------------------------------------------------------------------------+ + | z | :math:`z` position between :math:`0` and :math:`2\pi` (excluding the last point) | + +----------------+------------------------------------------------------------------------------------+ + | pi π | :math:`3.1415\ldots` | + +----------------+------------------------------------------------------------------------------------+ + | is_periodic_y | :math:`1` in core region where Y is periodic. :math:`0` otherwise | + +----------------+------------------------------------------------------------------------------------+ By default, :math:`x` is defined as ``(i+0.5) / (nx - 2*MXG)``, where ``MXG`` @@ -172,7 +174,7 @@ expressions. .. _tab-initexprfunc: .. table:: Initialisation expression functions - +------------------------------------------+------------------------------------------------------+ + +------------------------------------------+------------------------------------------------------+ | Name | Description | +==========================================+======================================================+ | ``abs(x)`` | Absolute value :math:`|x|` | @@ -238,8 +240,8 @@ In addition there are some special functions which enable control flow .. _tab-exprcontrol: .. table:: Control flow and special functions - - +------------------------------------------+------------------------------------------------------+ + + +------------------------------------------+------------------------------------------------------+ | Name | Description | +==========================================+======================================================+ | ``where(expr, gt0, lt0)`` | If the first ``expr`` evaluates to a value greater | @@ -258,8 +260,8 @@ In addition there are some special functions which enable control flow | | is evaluated before the ``expr`` expression. | | | Example: ``[n=2]( {n}^{n} )`` is ``2^2``. | +------------------------------------------+------------------------------------------------------+ - - + + For field-aligned tokamak simulations, the Y direction is along the field and in the core this will have a discontinuity at the twist-shift location where field-lines are matched onto each other. To handle this, @@ -329,13 +331,13 @@ to ``generate`` in the ``Context`` object. :: Field3D shear = ...; // Value calculated in BOUT++ - + FieldFactory factory(mesh); auto gen = factory->parse("model:viscosity"); Field3D viscosity; viscosity.allocate(); - + BOUT_FOR(i, viscosity.region("RGN_ALL")) { viscosity[i] = gen->generate(bout::generator::Context(i, CELL_CENTRE, mesh, 0.0) .set("shear", shear[i])); @@ -366,7 +368,7 @@ call functions, as in the above example ``viscosity`` is a function of which uses ``{arg}`` as the input value. We could then call this function: .. code-block:: cfg - + result = [arg = x*2](mycosh) @@ -381,7 +383,7 @@ occur. Recursive functions can however be enabled by setting ``input:max_recursion_depth != 0`` e.g.: .. code-block:: cfg - + [input] max_recursion_depth = 10 # 0 = none, -1 = unlimited @@ -408,7 +410,7 @@ recursion is used. Note: Use of this facility in general is not encouraged, as it can easily lead to very inefficient and hard to understand code. It is here because occasionally it might be necessary, and because making -the input language Turing complete was irresistible. +the input language Turing complete was irresistible. Initalising variables with the ``FieldFactory`` class ----------------------------------------------------- @@ -457,7 +459,7 @@ containing values which can be used in expressions, in particular `x`, `Context` object, allowing data from BOUT++ to be used in expressions. There are also ways to manipulate `Context` objects for more complex expressions and functions, see below for details. - + All classes inheriting from `FieldGenerator` must implement a `FieldGenerator::generate` function, which returns the value at the given ``(x,y,z,t)`` position. Classes should also implement @@ -585,9 +587,9 @@ the same value. This can be one of: - -2 for a symbol (e.g. “sinh”, “x” or “pi”). This includes anything which starts with a letter, and contains only letters, numbers, and underscores. The string is stored in the variable ``string curident`` - + - -3 for a ``Context`` parameter which appeared surrounded by braces ``{}``. - + - 0 to mean end of input - The character if none of the above. Since letters and numbers are diff --git a/output.make b/output.make deleted file mode 100644 index eae6b1be64..0000000000 --- a/output.make +++ /dev/null @@ -1,22 +0,0 @@ -BOUT_TOP ?= . - -include make.config - -# These expand to literals of the same name, which are then -# set in the bout.config script. This enables -# the final paths to the include and lib files to be set -# in the make/make install targets - -BOUT_INCLUDE_PATH="\$$BOUT_INCLUDE_PATH" -BOUT_LIB_PATH="\$$BOUT_LIB_PATH" -MPARK_VARIANT_INCLUDE_PATH="\$$MPARK_VARIANT_INCLUDE_PATH" -FMT_INCLUDE_PATH="\$$FMT_INCLUDE_PATH" - -.PHONY: cflags -cflags: - @echo $(BOUT_INCLUDE) $(BOUT_FLAGS) - -.PHONY: ldflags -ldflags: - @echo $(LDFLAGS) $(BOUT_LIBS) - diff --git a/pyproject.toml b/pyproject.toml index a87e8bb674..3e75bb39c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,3 +4,25 @@ requires = [] # Defined by PEP 517: build-backend = "backend" backend-path = ["tools/pylib/_boutpp_build/"] + +[dependency-groups] +dev = [ + "cmake~=4.2", + "clang-format~=22.0", + "clang-tidy~=22.0", + "clangd~=22.0", + "clangd-tidy~=1.1", + "cmakelang~=0.6", + "prek>=0.3.10", + "pyyaml~=6.0", + "ruff~=0.15", + "sphinx-lint~=1.0", + "sync-with-uv~=0.5.0", +] +maint = [ + "pygithub~=2.8", + "ruamel-yaml~=0.19", + "Unidecode~=1.3", +] + +[tool.uv.workspace] diff --git a/requirements.txt b/requirements.txt index 52d3076d58..3f31120881 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,8 @@ Jinja2>=3.1.4 numpy>=2.0.0 -scipy>=1.14.1 +scipy >= 1.14.0 netcdf4>=1.7.1 -matplotlib>=3.7.0 +matplotlib >= 3.9.0 Cython>=3.0.0 -boututils>=0.2.1 -boutdata>=0.2.1 -zoidberg>=0.2.2 +boutdata>=0.3.0 +zoidberg>=0.3.0 diff --git a/requirements_dev.txt b/requirements_dev.txt new file mode 100644 index 0000000000..ba0eeb91e2 --- /dev/null +++ b/requirements_dev.txt @@ -0,0 +1,6 @@ +clang-format~=22.0 +clang-tidy~=22.0 +cmakelang~=0.6 +pre-commit>=4.5.1 +ruff +sphinx-lint~=1.0 diff --git a/requirements_maint.txt b/requirements_maint.txt index 9f4ddc3699..be76672fd4 100644 --- a/requirements_maint.txt +++ b/requirements_maint.txt @@ -1,3 +1,3 @@ -pygithub~=2.4 -ruamel-yaml~=0.18 +pygithub~=2.9 +ruamel-yaml~=0.19 Unidecode~=1.3 diff --git a/src/bout++.cxx b/src/bout++.cxx index 0ec5c6fc61..b6989b00b3 100644 --- a/src/bout++.cxx +++ b/src/bout++.cxx @@ -4,7 +4,7 @@ * Adapted from the BOUT code by B.Dudson, University of York, Oct 2007 * ************************************************************************** - * Copyright 2010-2023 BOUT++ contributors + * Copyright 2010-2025 BOUT++ contributors * * Contact Ben Dudson, dudson2@llnl.gov * @@ -27,21 +27,28 @@ #include "bout/build_config.hxx" -const char DEFAULT_DIR[] = "data"; +static constexpr auto DEFAULT_DIR = "data"; #define GLOBALORIGIN #include "bout++-time.hxx" +#include "bout/array.hxx" #include "bout/boundary_factory.hxx" +#include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" #include "bout/coordinates_accessor.hxx" +#include "bout/dcomplex.hxx" +#include "bout/globals.hxx" #include "bout/hyprelib.hxx" #include "bout/interpolation_xz.hxx" #include "bout/interpolation_z.hxx" #include "bout/invert/laplacexz.hxx" #include "bout/invert_laplace.hxx" #include "bout/invert_parderiv.hxx" +#include "bout/mask.hxx" +#include "bout/monitor.hxx" #include "bout/mpi_wrapper.hxx" #include "bout/msg_stack.hxx" #include "bout/openmpwrap.hxx" @@ -52,7 +59,9 @@ const char DEFAULT_DIR[] = "data"; #include "bout/rkscheme.hxx" #include "bout/slepclib.hxx" #include "bout/solver.hxx" +#include "bout/sys/gettext.hxx" #include "bout/sys/timer.hxx" +#include "bout/utils.hxx" #include "bout/version.hxx" #define BOUT_NO_USING_NAMESPACE_BOUTGLOBALS @@ -63,15 +72,25 @@ const char DEFAULT_DIR[] = "data"; #include "bout/adios_object.hxx" #endif +#include #include +#include +#include #include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include -#include - // Value passed at compile time // Used for MD5SUM, BOUT_LOCALE_PATH, and REVISION #define BUILDFLAG1_(x) #x @@ -81,12 +100,6 @@ const char DEFAULT_DIR[] = "data"; #define INDIRECT0_BOUTMAIN(...) INDIRECT1_BOUTMAIN(#__VA_ARGS__) #define STRINGIFY(a) INDIRECT0_BOUTMAIN(a) -// Define S_ISDIR if not defined by system headers (that is, MSVC) -// Taken from https://github.com/curl/curl/blob/e59540139a398dc70fde6aec487b19c5085105af/lib/curl_setup.h#L748-L751 -#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) -#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) -#endif - #ifdef _MSC_VER #include inline auto getpid() -> int { return GetCurrentProcessId(); } @@ -126,78 +139,72 @@ int BoutInitialise(int& argc, char**& argv) { try { args = parseCommandLineArgs(argc, argv); } catch (const BoutException& e) { - output_error << _("Bad command line arguments:\n") << e.what() << std::endl; + output_error.write("{:s}{:s}\n", _("Bad command line arguments:\n"), e.what()); return 1; } - try { - checkDataDirectoryIsAccessible(args.data_dir); + checkDataDirectoryIsAccessible(args.data_dir); - // Set the command-line arguments - SlepcLib::setArgs(argc, argv); // SLEPc initialisation - PetscLib::setArgs(argc, argv); // PETSc initialisation - Solver::setArgs(argc, argv); // Solver initialisation - BoutComm::setArgs(argc, argv); // MPI initialisation + // Set the command-line arguments + SlepcLib::setArgs(argc, argv); // SLEPc initialisation + PetscLib::setArgs(argc, argv); // PETSc initialisation + Solver::setArgs(argc, argv); // Solver initialisation + BoutComm::setArgs(argc, argv); // MPI initialisation - const int MYPE = BoutComm::rank(); + const int MYPE = BoutComm::rank(); - setupBoutLogColor(args.color_output, MYPE); + setupBoutLogColor(args.color_output, MYPE); - setupOutput(args.data_dir, args.log_file, args.verbosity, MYPE); + setupOutput(args.data_dir, args.log_file, args.verbosity, MYPE); - savePIDtoFile(args.data_dir, MYPE); + savePIDtoFile(args.data_dir, MYPE); #if BOUT_HAS_ADIOS2 - bout::ADIOSInit(BoutComm::get()); + bout::ADIOSInit(BoutComm::get()); #endif - // Print the different parts of the startup info - printStartupHeader(MYPE, BoutComm::size()); - printCompileTimeOptions(); - printCommandLineArguments(args.original_argv); - - // Load settings file - OptionsReader* reader = OptionsReader::getInstance(); - // Ideally we'd use the long options for `datadir` and - // `optionfile` here, but we'd need to call parseCommandLine - // _first_ in order to do that and set the source, etc., but we - // need to call that _second_ in order to override the input file - reader->read(Options::getRoot(), "{}/{}", args.data_dir, args.opt_file); - - // Get options override from command-line - reader->parseCommandLine(Options::getRoot(), args.argv); - - // Get the variables back out so they count as having been used - // when checking for unused options. They normally _do_ get used, - // but it's possible that only happens in BoutFinalise, which is - // too late for that check. - const auto datadir = Options::root()["datadir"].withDefault(DEFAULT_DIR); - [[maybe_unused]] const auto optionfile = - Options::root()["optionfile"].withDefault(args.opt_file); - const auto settingsfile = - Options::root()["settingsfile"].withDefault(args.set_file); - - setRunStartInfo(Options::root()); - - if (MYPE == 0) { - writeSettingsFile(Options::root(), datadir, settingsfile); - } - - bout::globals::mpi = new MpiWrapper(); + // Print the different parts of the startup info + printStartupHeader(MYPE, BoutComm::size()); + printCompileTimeOptions(); + printCommandLineArguments(args.original_argv); + + // Load settings file + OptionsReader* reader = OptionsReader::getInstance(); + // Ideally we'd use the long options for `datadir` and + // `optionfile` here, but we'd need to call parseCommandLine + // _first_ in order to do that and set the source, etc., but we + // need to call that _second_ in order to override the input file + reader->read(Options::getRoot(), "{}", (args.data_dir / args.opt_file).string()); + + // Get options override from command-line + reader->parseCommandLine(Options::getRoot(), args.argv); + + // Get the variables back out so they count as having been used + // when checking for unused options. They normally _do_ get used, + // but it's possible that only happens in BoutFinalise, which is + // too late for that check. + const auto datadir = Options::root()["datadir"].withDefault(DEFAULT_DIR); + [[maybe_unused]] const auto optionfile = + Options::root()["optionfile"].withDefault(args.opt_file); + const auto settingsfile = + Options::root()["settingsfile"].withDefault(args.set_file); + + setRunStartInfo(Options::root()); + + if (MYPE == 0) { + writeSettingsFile(Options::root(), datadir, settingsfile); + } - // Create the mesh - bout::globals::mesh = Mesh::create(); - // Load from sources. Required for Field initialisation - bout::globals::mesh->load(); + bout::globals::mpi = new MpiWrapper(); - // time_report options are used in BoutFinalise, i.e. after we - // check for unused options - Options::root()["time_report"].setConditionallyUsed(); + // Create the mesh + bout::globals::mesh = Mesh::create(); + // Load from sources. Required for Field initialisation + bout::globals::mesh->load(); - } catch (const BoutException& e) { - output_error.write(_("Error encountered during initialisation: {:s}\n"), e.what()); - throw; - } + // time_report options are used in BoutFinalise, i.e. after we + // check for unused options + Options::root()["time_report"].setConditionallyUsed(); return 0; } @@ -318,8 +325,8 @@ template // Now we can print all the options used in constructing our // type. Note that this does require all the options are used in the // constructor, and not in a `init` method or similar - std::cout << fmt::format("Input options for {} '{}':\n\n", Factory::type_name, type); - std::cout << fmt::format("{:id}\n", help_options); + fmt::println("Input options for {:s} '{}':\n", Factory::type_name, type); + fmt::println("{:id}", help_options); std::exit(EXIT_SUCCESS); } @@ -339,7 +346,7 @@ void handleFactoryHelp(const std::string& current_arg, int i, int argc, char** a if (current_arg == help_arg) { if (i + 1 >= argc) { - throw BoutException(_("Usage is {} {} \n"), argv[0], help_arg); + throw BoutException(_f("Usage is {} {} \n"), argv[0], help_arg); } printTypeOptions(argv[i + 1]); } @@ -353,9 +360,10 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { if (current_arg == "-h" || current_arg == "--help") { // Print help message -- note this will be displayed once per processor as we've not // started MPI yet. - output.write(_("Usage: {:s} [-d ] [-f ] [restart " - "[append]] [VAR=VALUE]\n"), - argv[0]); + output.write( + _f("Usage: {:s} [-d ] [-f ] [restart " + "[append]] [VAR=VALUE]\n"), + argv[0]); output.write( _("\n" " -d \t\tLook in for input/output files\n" @@ -368,33 +376,33 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { output.write(_(" -c, --color\t\t\tColor output using bout-log-color\n")); #endif output.write( - _(" --print-config\t\tPrint the compile-time configuration\n" - " --list-solvers\t\tList the available time solvers\n" - " --help-solver \tPrint help for the given time solver\n" - " --list-laplacians\t\tList the available Laplacian inversion solvers\n" - " --help-laplacian \tPrint help for the given Laplacian " - "inversion solver\n" - " --list-laplacexz\t\tList the available LaplaceXZ inversion solvers\n" - " --help-laplacexz \tPrint help for the given LaplaceXZ " - "inversion solver\n" - " --list-invertpars\t\tList the available InvertPar solvers\n" - " --help-invertpar \tPrint help for the given InvertPar solver\n" - " --list-rkschemes\t\tList the available Runge-Kutta schemes\n" - " --help-rkscheme \tPrint help for the given Runge-Kutta scheme\n" - " --list-meshes\t\t\tList the available Meshes\n" - " --help-mesh \t\tPrint help for the given Mesh\n" - " --list-xzinterpolations\tList the available XZInterpolations\n" - " --help-xzinterpolation \tPrint help for the given " - "XZInterpolation\n" - " --list-zinterpolations\tList the available ZInterpolations\n" - " --help-zinterpolation \tPrint help for the given " - "ZInterpolation\n" - " -h, --help\t\t\tThis message\n" - " restart [append]\t\tRestart the simulation. If append is specified, " - "append to the existing output files, otherwise overwrite them\n" - " VAR=VALUE\t\t\tSpecify a VALUE for input parameter VAR\n" - "\nFor all possible input parameters, see the user manual and/or the " - "physics model source (e.g. {:s}.cxx)\n"), + _f(" --print-config\t\tPrint the compile-time configuration\n" + " --list-solvers\t\tList the available time solvers\n" + " --help-solver \tPrint help for the given time solver\n" + " --list-laplacians\t\tList the available Laplacian inversion solvers\n" + " --help-laplacian \tPrint help for the given Laplacian " + "inversion solver\n" + " --list-laplacexz\t\tList the available LaplaceXZ inversion solvers\n" + " --help-laplacexz \tPrint help for the given LaplaceXZ " + "inversion solver\n" + " --list-invertpars\t\tList the available InvertPar solvers\n" + " --help-invertpar \tPrint help for the given InvertPar solver\n" + " --list-rkschemes\t\tList the available Runge-Kutta schemes\n" + " --help-rkscheme \tPrint help for the given Runge-Kutta scheme\n" + " --list-meshes\t\t\tList the available Meshes\n" + " --help-mesh \t\tPrint help for the given Mesh\n" + " --list-xzinterpolations\tList the available XZInterpolations\n" + " --help-xzinterpolation \tPrint help for the given " + "XZInterpolation\n" + " --list-zinterpolations\tList the available ZInterpolations\n" + " --help-zinterpolation \tPrint help for the given " + "ZInterpolation\n" + " -h, --help\t\t\tThis message\n" + " restart [append]\t\tRestart the simulation. If append is specified, " + "append to the existing output files, otherwise overwrite them\n" + " VAR=VALUE\t\t\tSpecify a VALUE for input parameter VAR\n" + "\nFor all possible input parameters, see the user manual and/or the " + "physics model source (e.g. {:s}.cxx)\n"), argv[0]); std::exit(EXIT_SUCCESS); @@ -425,7 +433,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { if (string(argv[i]) == "-d") { // Set data directory if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -d \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -d \n"), argv[0]); } args.data_dir = argv[++i]; @@ -434,7 +442,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } else if (string(argv[i]) == "-f") { // Set options file if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -f \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -f \n"), argv[0]); } args.opt_file = argv[++i]; @@ -443,7 +451,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } else if (string(argv[i]) == "-o") { // Set options file if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -o \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -o \n"), argv[0]); } args.set_file = argv[++i]; @@ -452,7 +460,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } else if ((string(argv[i]) == "-l") || (string(argv[i]) == "--log")) { // Set log file if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -l \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -l \n"), argv[0]); } args.log_file = argv[++i]; @@ -488,13 +496,12 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } void checkDataDirectoryIsAccessible(const std::string& data_dir) { - struct stat test; - if (stat(data_dir.c_str(), &test) == 0) { - if (!S_ISDIR(test.st_mode)) { - throw BoutException(_("DataDir \"{:s}\" is not a directory\n"), data_dir); + if (std::filesystem::exists(data_dir)) { + if (!std::filesystem::is_directory(data_dir)) { + throw BoutException(_f("DataDir \"{:s}\" is not a directory\n"), data_dir); } } else { - throw BoutException(_("DataDir \"{:s}\" does not exist or is not accessible\n"), + throw BoutException(_f("DataDir \"{:s}\" does not exist or is not accessible\n"), data_dir); } } @@ -506,7 +513,7 @@ void savePIDtoFile(const std::string& data_dir, int MYPE) { pid_file.open(filename.str(), std::ios::out | std::ios::trunc); if (not pid_file.is_open()) { - throw BoutException(_("Could not create PID file {:s}"), filename.str()); + throw BoutException(_f("Could not create PID file {:s}"), filename.str()); } pid_file << getpid() << "\n"; @@ -514,17 +521,17 @@ void savePIDtoFile(const std::string& data_dir, int MYPE) { } void printStartupHeader(int MYPE, int NPES) { - output_progress.write(_("BOUT++ version {:s}\n"), bout::version::full); - output_progress.write(_("Revision: {:s}\n"), bout::version::revision); + output_progress.write(_f("BOUT++ version {:s}\n"), bout::version::full); + output_progress.write(_f("Revision: {:s}\n"), bout::version::revision); #ifdef MD5SUM output_progress.write("MD5 checksum: {:s}\n", BUILDFLAG(MD5SUM)); #endif - output_progress.write(_("Code compiled on {:s} at {:s}\n\n"), boutcompiledate, + output_progress.write(_f("Code compiled on {:s} at {:s}\n\n"), boutcompiledate, boutcompiletime); output_info.write("B.Dudson (University of York), M.Umansky (LLNL) 2007\n"); output_info.write("Based on BOUT by Xueqiao Xu, 1999\n\n"); - output_info.write(_("Processor number: {:d} of {:d}\n\n"), MYPE, NPES); + output_info.write(_f("Processor number: {:d} of {:d}\n\n"), MYPE, NPES); output_info.write("pid: {:d}\n\n", getpid()); } @@ -534,55 +541,51 @@ void printCompileTimeOptions() { using namespace bout::build; - output_info.write(_("\tRuntime error checking {}"), is_enabled(check_level > 0)); + output_info.write(_f("\tRuntime error checking {}"), is_enabled(check_level > 0)); if (check_level > 0) { - output_info.write(_(", level {}"), check_level); + output_info.write(_f(", level {}"), check_level); } output_info.write("\n"); #ifdef PNCDF - output_info.write(_("\tParallel NetCDF support enabled\n")); + output_info.write(_f("\tParallel NetCDF support enabled\n")); #else - output_info.write(_("\tParallel NetCDF support disabled\n")); + output_info.write(_f("\tParallel NetCDF support disabled\n")); #endif - output_info.write(_("\tMetrics mode is {}\n"), use_metric_3d ? "3D" : "2D"); - output_info.write(_("\tFFT support {}\n"), is_enabled(has_fftw)); - output_info.write(_("\tNatural language support {}\n"), is_enabled(has_gettext)); - output_info.write(_("\tLAPACK support {}\n"), is_enabled(has_lapack)); + output_info.write(_f("\tMetrics mode is {}\n"), use_metric_3d ? "3D" : "2D"); + output_info.write(_f("\tFFT support {}\n"), is_enabled(has_fftw)); + output_info.write(_f("\tNatural language support {}\n"), is_enabled(has_gettext)); + output_info.write(_f("\tLAPACK support {}\n"), is_enabled(has_lapack)); // Horrible nested ternary to set this at compile time constexpr auto netcdf_flavour = has_netcdf ? (has_legacy_netcdf ? " (Legacy)" : " (NetCDF4)") : ""; - output_info.write(_("\tNetCDF support {}{}\n"), is_enabled(has_netcdf), netcdf_flavour); - output_info.write(_("\tADIOS2 support {}\n"), is_enabled(has_adios2)); - output_info.write(_("\tPETSc support {}\n"), is_enabled(has_petsc)); - output_info.write(_("\tPretty function name support {}\n"), - is_enabled(has_pretty_function)); - output_info.write(_("\tPVODE support {}\n"), is_enabled(has_pvode)); - output_info.write(_("\tScore-P support {}\n"), is_enabled(has_scorep)); - output_info.write(_("\tSLEPc support {}\n"), is_enabled(has_slepc)); - output_info.write(_("\tSUNDIALS support {}\n"), is_enabled(has_sundials)); - output_info.write(_("\tBacktrace in exceptions {}\n"), is_enabled(use_backtrace)); - output_info.write(_("\tColour in logs {}\n"), is_enabled(use_color)); - output_info.write(_("\tOpenMP parallelisation {}, using {} threads\n"), + output_info.write(_f("\tNetCDF support {}{}\n"), is_enabled(has_netcdf), + netcdf_flavour); + output_info.write(_f("\tADIOS2 support {}\n"), is_enabled(has_adios2)); + output_info.write(_f("\tPETSc support {}\n"), is_enabled(has_petsc)); + output_info.write(_f("\tPVODE support {}\n"), is_enabled(has_pvode)); + output_info.write(_f("\tScore-P support {}\n"), is_enabled(has_scorep)); + output_info.write(_f("\tSLEPc support {}\n"), is_enabled(has_slepc)); + output_info.write(_f("\tSUNDIALS support {}\n"), is_enabled(has_sundials)); + output_info.write(_f("\tBacktrace in exceptions {}\n"), is_enabled(use_backtrace)); + output_info.write(_f("\tColour in logs {}\n"), is_enabled(use_color)); + output_info.write(_f("\tOpenMP parallelisation {}, using {} threads\n"), is_enabled(use_openmp), omp_get_max_threads()); - output_info.write(_("\tExtra debug output {}\n"), is_enabled(use_output_debug)); - output_info.write(_("\tFloating-point exceptions {}\n"), is_enabled(use_sigfpe)); - output_info.write(_("\tSignal handling support {}\n"), is_enabled(use_signal)); - output_info.write(_("\tField name tracking {}\n"), is_enabled(use_track)); - output_info.write(_("\tMessage stack {}\n"), is_enabled(use_msgstack)); + output_info.write(_f("\tExtra debug output {}\n"), is_enabled(use_output_debug)); + output_info.write(_f("\tFloating-point exceptions {}\n"), is_enabled(use_sigfpe)); + output_info.write(_f("\tSignal handling support {}\n"), is_enabled(use_signal)); + output_info.write(_f("\tField name tracking {}\n"), is_enabled(use_track)); + output_info.write(_f("\tMessage stack {}\n"), is_enabled(use_msgstack)); // The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings // which could cause problems (e.g. terminate strings). - output_info.write(_("\tCompiled with flags : {:s}\n"), STRINGIFY(BOUT_FLAGS_STRING)); + output_info.write(_f("\tCompiled with flags : {:s}\n"), STRINGIFY(BOUT_FLAGS_STRING)); } void printCommandLineArguments(const std::vector& original_argv) { - output_info.write(_("\tCommand line options for this run : ")); - for (auto& arg : original_argv) { - output_info << arg << " "; - } - output_info.write("\n"); + output_info.write("{:s}{}\n", _("\tCommand line options for this run : "), + fmt::join(original_argv, " ")); } bool setupBoutLogColor(bool color_output, int MYPE) { @@ -616,7 +619,8 @@ bool setupBoutLogColor(bool color_output, int MYPE) { } if (!success) { // Failed . Probably not important enough to stop the simulation - std::cerr << _("Could not run bout-log-color. Make sure it is in your PATH\n"); + fmt::print(stderr, "{:s}", + _("Could not run bout-log-color. Make sure it is in your PATH\n")); } return success; } @@ -636,7 +640,7 @@ void setupOutput(const std::string& data_dir, const std::string& log_file, int v /// Open an output file to echo everything to /// On processor 0 anything written to output will go to stdout and the file if (output.open("{:s}/{:s}.{:d}", data_dir, log_file, MYPE)) { - throw BoutException(_("Could not open {:s}/{:s}.{:d} for writing"), data_dir, + throw BoutException(_f("Could not open {:s}/{:s}.{:d} for writing"), data_dir, log_file, MYPE); } } @@ -686,7 +690,6 @@ void addBuildFlagsToOptions(Options& options) { options["has_umpire"].force(bout::build::has_umpire); options["has_caliper"].force(bout::build::has_caliper); options["has_raja"].force(bout::build::has_raja); - options["has_pretty_function"].force(bout::build::has_pretty_function); options["has_pvode"].force(bout::build::has_pvode); options["has_scorep"].force(bout::build::has_scorep); options["has_slepc"].force(bout::build::has_slepc); @@ -730,7 +733,7 @@ int BoutFinalise(bool write_settings) { writeSettingsFile(options, data_dir, set_file); } } catch (const BoutException& e) { - output_error << _("Error whilst writing settings") << e.what() << endl; + output_error.write("{} {}\n", _("Error whilst writing settings"), e.what()); } } @@ -887,7 +890,7 @@ int BoutMonitor::call(Solver* solver, BoutReal t, [[maybe_unused]] int iter, int BoutReal t_remain = mpi_start_time + wall_limit - bout::globals::mpi->MPI_Wtime(); if (t_remain < run_data.wtime * 2) { // Less than 2 time-steps left - output_warn.write(_("Only {:e} seconds ({:.2f} steps) left. Quitting\n"), t_remain, + output_warn.write(_f("Only {:e} seconds ({:.2f} steps) left. Quitting\n"), t_remain, t_remain / run_data.wtime); user_requested_exit = true; } else { diff --git a/src/field/field.cxx b/src/field/field.cxx index e48a8f3ef7..e9b01f0bcb 100644 --- a/src/field/field.cxx +++ b/src/field/field.cxx @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -39,3 +38,14 @@ int Field::getNx() const { return getMesh()->LocalNx; } int Field::getNy() const { return getMesh()->LocalNy; } int Field::getNz() const { return getMesh()->LocalNz; } + +bool Field::isFci() const { + const auto* coords = this->getCoordinates(); + if (coords == nullptr) { + return false; + } + if (not coords->hasParallelTransform()) { + return false; + } + return not coords->getParallelTransform().canToFromFieldAligned(); +} diff --git a/src/field/field2d.cxx b/src/field/field2d.cxx index c8b9ebb689..61799e3444 100644 --- a/src/field/field2d.cxx +++ b/src/field/field2d.cxx @@ -25,30 +25,26 @@ * **************************************************************************/ -#include "bout/build_config.hxx" - -#include -#include - -#include // for mesh - -#include - -#include +#include "bout/bout_types.hxx" +#include "bout/build_defines.hxx" +#include "bout/unused.hxx" +#include #include #include - +#include #include +#include +#include // for mesh #include -#include - #include -#include -#include +#include +#include +#include -Field2D::Field2D(Mesh* localmesh, CELL_LOC location_in, DirectionTypes directions_in) +Field2D::Field2D(Mesh* localmesh, CELL_LOC location_in, DirectionTypes directions_in, + [[maybe_unused]] std::optional regionID) : Field(localmesh, location_in, directions_in) { if (fieldmesh) { @@ -62,7 +58,6 @@ Field2D::Field2D(Mesh* localmesh, CELL_LOC location_in, DirectionTypes direction } Field2D::Field2D(const Field2D& f) : Field(f), data(f.data) { - TRACE("Field2D(Field2D&)"); #if BOUT_USE_TRACK name = f.name; @@ -145,8 +140,6 @@ Field2D& Field2D::operator=(const Field2D& rhs) { return (*this); // skip this assignment } - TRACE("Field2D: Assignment from Field2D"); - Field::operator=(rhs); // Copy the data and data sizes @@ -165,8 +158,6 @@ Field2D& Field2D::operator=(Field2D&& rhs) noexcept { return (*this); // skip this assignment } - TRACE("Field2D: Move assignment from Field2D"); - // Move the data and data sizes nx = rhs.nx; ny = rhs.ny; @@ -185,7 +176,6 @@ Field2D& Field2D::operator=(const BoutReal rhs) { name = ""; #endif - TRACE("Field2D = BoutReal"); allocate(); BOUT_FOR(i, getRegion("RGN_ALL")) { (*this)[i] = rhs; } @@ -196,7 +186,6 @@ Field2D& Field2D::operator=(const BoutReal rhs) { ///////////////////// BOUNDARY CONDITIONS ////////////////// void Field2D::applyBoundary(bool init) { - TRACE("Field2D::applyBoundary()"); #if CHECK > 0 if (init) { @@ -218,7 +207,6 @@ void Field2D::applyBoundary(bool init) { } void Field2D::applyBoundary(BoutReal time) { - TRACE("Field2D::applyBoundary(time)"); #if CHECK > 0 if (not isBoundarySet()) { @@ -234,7 +222,6 @@ void Field2D::applyBoundary(BoutReal time) { } void Field2D::applyBoundary(const std::string& condition) { - TRACE("Field2D::applyBoundary(condition)"); checkData(*this); @@ -268,7 +255,7 @@ void Field2D::applyBoundary(const std::string& condition) { } void Field2D::applyBoundary(const std::string& region, const std::string& condition) { - TRACE("Field2D::applyBoundary(string, string)"); + checkData(*this); /// Get the boundary factory (singleton) @@ -310,7 +297,6 @@ void Field2D::applyBoundary(const std::string& region, const std::string& condit } void Field2D::applyTDerivBoundary() { - TRACE("Field2D::applyTDerivBoundary()"); checkData(*this); ASSERT1(deriv != nullptr); @@ -322,15 +308,15 @@ void Field2D::applyTDerivBoundary() { } void Field2D::setBoundaryTo(const Field2D& f2d) { - TRACE("Field2D::setBoundary(const Field2D&)"); checkData(f2d); allocate(); // Make sure data allocated /// Loop over boundary regions - for (const auto& reg : fieldmesh->getBoundaries()) { + for (const auto& regnew : fieldmesh->getBoundaries()) { /// Loop within each region + auto* reg = regnew->getLegacyPointer(); for (reg->first(); !reg->isDone(); reg->next()) { // Get value half-way between cells BoutReal val = @@ -341,10 +327,9 @@ void Field2D::setBoundaryTo(const Field2D& f2d) { } } -////////////// NON-MEMBER OVERLOADED OPERATORS ////////////// +void Field2D::swapData(Field2D& other) { std::swap(data, other.data); } -// Unary minus -Field2D operator-(const Field2D& f) { return -1.0 * f; } +////////////// NON-MEMBER OVERLOADED OPERATORS ////////////// //////////////// NON-MEMBER FUNCTIONS ////////////////// diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 0d2bc0694e..9e459cc97d 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -4,10 +4,10 @@ * Class for 3D X-Y-Z scalar fields * ************************************************************************** - * Copyright 2010 - 2025 BOUT++ developers + * Copyright 2010 - 2026 BOUT++ developers * * Contact: Ben Dudson, dudson2@llnl.gov - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -25,31 +25,42 @@ * **************************************************************************/ -#include "bout/build_config.hxx" +#include "bout/bout_types.hxx" +#include "bout/build_defines.hxx" +#include "bout/index_derivs_interface.hxx" #include #include #include +#include +#include +#include +#include +#include #include "bout/parallel_boundary_op.hxx" #include "bout/parallel_boundary_region.hxx" #include #include #include +#include #include #include #include #include #include #include -#include +#include #include #include +#include "fmt/format.h" + /// Constructor -Field3D::Field3D(Mesh* localmesh, CELL_LOC location_in, DirectionTypes directions_in) - : Field(localmesh, location_in, directions_in) { +Field3D::Field3D(Mesh* localmesh, CELL_LOC location_in, DirectionTypes directions_in, + std::optional regionID) + : Field(localmesh, location_in, directions_in), regionID(regionID) { #if BOUT_USE_TRACK name = ""; #endif @@ -67,8 +78,6 @@ Field3D::Field3D(const Field3D& f) : Field(f), data(f.data), yup_fields(f.yup_fields), ydown_fields(f.ydown_fields), regionID(f.regionID) { - TRACE("Field3D(Field3D&)"); - if (fieldmesh) { nx = fieldmesh->LocalNx; ny = fieldmesh->LocalNy; @@ -76,9 +85,39 @@ Field3D::Field3D(const Field3D& f) } } -Field3D::Field3D(const Field2D& f) : Field(f) { +Field3D operator+(const Field2D& lhs, const Field3DParallel& rhs) { + return lhs + rhs.asField3D(); +} + +Field3D operator-(const Field2D& lhs, const Field3DParallel& rhs) { + return lhs - rhs.asField3D(); +} + +Field3D operator*(const Field2D& lhs, const Field3DParallel& rhs) { + return lhs * rhs.asField3D(); +} - TRACE("Field3D: Copy constructor from Field2D"); +Field3D operator/(const Field2D& lhs, const Field3DParallel& rhs) { + return lhs / rhs.asField3D(); +} + +Field3D operator+(const Field3DParallel& lhs, const Field2D& rhs) { + return lhs.asField3D() + rhs; +} + +Field3D operator-(const Field3DParallel& lhs, const Field2D& rhs) { + return lhs.asField3D() - rhs; +} + +Field3D operator*(const Field3DParallel& lhs, const Field2D& rhs) { + return lhs.asField3D() * rhs; +} + +Field3D operator/(const Field3DParallel& lhs, const Field2D& rhs) { + return lhs.asField3D() / rhs; +} + +Field3D::Field3D(const Field2D& f) : Field(f) { nx = fieldmesh->LocalNx; ny = fieldmesh->LocalNy; @@ -89,15 +128,25 @@ Field3D::Field3D(const Field2D& f) : Field(f) { Field3D::Field3D(const BoutReal val, Mesh* localmesh) : Field3D(localmesh) { - TRACE("Field3D: Copy constructor from value"); + *this = val; +} + +Field3DParallel::Field3DParallel(const BoutReal val, Mesh* localmesh) + : Field3D(localmesh) { *this = val; + if (this->isFci()) { + splitParallelSlices(); + for (size_t i = 0; i < numberParallelSlices(); ++i) { + yup(i) = val; + ydown(i) = val; + } + } } Field3D::Field3D(Array data_in, Mesh* localmesh, CELL_LOC datalocation, DirectionTypes directions_in) : Field(localmesh, datalocation, directions_in), data(std::move(data_in)) { - TRACE("Field3D: Copy constructor from Array and Mesh"); nx = fieldmesh->LocalNx; ny = fieldmesh->LocalNy; @@ -106,8 +155,6 @@ Field3D::Field3D(Array data_in, Mesh* localmesh, CELL_LOC datalocation ASSERT1(data.size() == nx * ny * nz); } -Field3D::~Field3D() { delete deriv; } - Field3D& Field3D::allocate() { if (data.empty()) { if (!fieldmesh) { @@ -137,7 +184,6 @@ Field3D* Field3D::timeDeriv() { } void Field3D::splitParallelSlices() { - TRACE("Field3D::splitParallelSlices"); if (hasParallelSlices()) { return; @@ -149,10 +195,10 @@ void Field3D::splitParallelSlices() { yup_fields.emplace_back(fieldmesh); ydown_fields.emplace_back(fieldmesh); } + resetRegionParallel(); } void Field3D::clearParallelSlices() { - TRACE("Field3D::clearParallelSlices"); if (!hasParallelSlices()) { return; @@ -234,7 +280,7 @@ const Region& Field3D::getRegion2D(const std::string& region_name) const } /*************************************************************** - * OPERATORS + * OPERATORS ***************************************************************/ /////////////////// ASSIGNMENT //////////////////// @@ -245,7 +291,7 @@ Field3D& Field3D::operator=(const Field3D& rhs) { return (*this); // skip this assignment } - TRACE("Field3D: Assignment from Field3D"); + track(rhs, "operator="); // Copy base slice Field::operator=(rhs); @@ -265,29 +311,8 @@ Field3D& Field3D::operator=(const Field3D& rhs) { return *this; } -Field3D& Field3D::operator=(Field3D&& rhs) { - TRACE("Field3D: Assignment from Field3D"); - - // Move parallel slices or delete existing ones. - yup_fields = std::move(rhs.yup_fields); - ydown_fields = std::move(rhs.ydown_fields); - - // Move the data and data sizes - nx = rhs.nx; - ny = rhs.ny; - nz = rhs.nz; - regionID = rhs.regionID; - - data = std::move(rhs.data); - - // Move base slice last - Field::operator=(std::move(rhs)); - - return *this; -} - Field3D& Field3D::operator=(const Field2D& rhs) { - TRACE("Field3D = Field2D"); + track(rhs, "operator="); /// Check that the data is allocated ASSERT1(rhs.isAllocated()); @@ -314,7 +339,6 @@ Field3D& Field3D::operator=(const Field2D& rhs) { } void Field3D::operator=(const FieldPerp& rhs) { - TRACE("Field3D = FieldPerp"); ASSERT1_FIELDS_COMPATIBLE(*this, rhs); /// Check that the data is allocated @@ -333,7 +357,7 @@ void Field3D::operator=(const FieldPerp& rhs) { } Field3D& Field3D::operator=(const BoutReal val) { - TRACE("Field3D = BoutReal"); + track(val, "operator="); // Delete existing parallel slices. We don't copy parallel slices, so any // that currently exist will be incorrect. @@ -343,19 +367,41 @@ Field3D& Field3D::operator=(const BoutReal val) { allocate(); BOUT_FOR(i, getRegion("RGN_ALL")) { (*this)[i] = val; } + this->name = "BR"; return *this; } -Field3D& Field3D::calcParallelSlices() { - getCoordinates()->getParallelTransform().calcParallelSlices(*this); +Field3DParallel& Field3DParallel::operator=(const BoutReal val) { + TRACE("Field3DParallel = BoutReal"); + track(val, "operator="); + + if (isFci()) { + if (!hasParallelSlices()) { + splitParallelSlices(); + } + for (size_t i = 0; i < numberParallelSlices(); ++i) { + yup(i) = val; + ydown(i) = val; + } + } + resetRegion(); + + allocate(); + + BOUT_FOR(i, getRegion("RGN_ALL")) { (*this)[i] = val; } + return *this; } +void Field3D::calcParallelSlices() { + ASSERT1(areCalcParallelSlicesAllowed()); + getCoordinates()->getParallelTransform().calcParallelSlices(*this); +} + ///////////////////// BOUNDARY CONDITIONS ////////////////// void Field3D::applyBoundary(bool init) { - TRACE("Field3D::applyBoundary()"); #if CHECK > 0 if (init) { @@ -378,7 +424,6 @@ void Field3D::applyBoundary(bool init) { } void Field3D::applyBoundary(BoutReal t) { - TRACE("Field3D::applyBoundary()"); #if CHECK > 0 if (not isBoundarySet()) { @@ -395,7 +440,6 @@ void Field3D::applyBoundary(BoutReal t) { } void Field3D::applyBoundary(const std::string& condition) { - TRACE("Field3D::applyBoundary(condition)"); checkData(*this); @@ -413,7 +457,7 @@ void Field3D::applyBoundary(const std::string& condition) { } void Field3D::applyBoundary(const std::string& region, const std::string& condition) { - TRACE("Field3D::applyBoundary(string, string)"); + checkData(*this); /// Get the boundary factory (singleton) @@ -439,7 +483,6 @@ void Field3D::applyBoundary(const std::string& region, const std::string& condit } void Field3D::applyTDerivBoundary() { - TRACE("Field3D::applyTDerivBoundary()"); checkData(*this); ASSERT1(deriv != nullptr); @@ -450,24 +493,81 @@ void Field3D::applyTDerivBoundary() { } } -void Field3D::setBoundaryTo(const Field3D& f3d) { - TRACE("Field3D::setBoundary(const Field3D&)"); +void Field3D::setBoundaryTo(const Field3D& f3d, bool copyParallelSlices, + bool forceLegacy) { checkData(f3d); allocate(); // Make sure data allocated - /// Loop over boundary regions - for (const auto& reg : fieldmesh->getBoundaries()) { - /// Loop within each region - for (reg->first(); !reg->isDone(); reg->next()) { - for (int z = 0; z < nz; z++) { - // Get value half-way between cells - BoutReal val = - 0.5 * (f3d(reg->x, reg->y, z) + f3d(reg->x - reg->bx, reg->y - reg->by, z)); - // Set to this value - (*this)(reg->x, reg->y, z) = - 2. * val - (*this)(reg->x - reg->bx, reg->y - reg->by, z); + if (isFci()) { + ASSERT1(f3d.hasParallelSlices()); + if (copyParallelSlices) { + splitParallelSlices(); + for (int i = 0; i < fieldmesh->ystart; ++i) { + yup(i) = f3d.yup(i); + ydown(i) = f3d.ydown(i); + } + } else { + // Set yup/ydown using midpoint values from f3d + ASSERT1(hasParallelSlices()); + + for (auto& region : fieldmesh->getBoundariesPar()) { + for (const auto& point : *region) { + // Interpolate midpoint value in f3d + const BoutReal val = point.interpolate_boundary_o2(f3d); + // Set the same boundary value in this field + point.dirichlet_o1(*this, val); + } + } + } + } + + // Non-FCI. + // Transform to field-aligned coordinates? + // Loop over boundary regions + for (const auto& newreg : fieldmesh->getBoundaries()) { + if (newreg->isX) { + bout::boundary::iter_boundary(newreg, [&](auto& point) { + const BoutReal val = point.interpolate_boundary_o2(f3d); + point.dirichlet_o2(*this, val); + }); + if (forceLegacy) { + // get the old, potentially wrong behaviour + auto* reg = newreg->getLegacyPointer(); + if (isFci() && reg->by != 0) { + continue; + } + /// Loop within each region + for (reg->first(); !reg->isDone(); reg->next()) { + for (int z = 0; z < nz; z++) { + // Get value half-way between cells + const BoutReal val = + 0.5 + * (f3d(reg->x, reg->y, z) + f3d(reg->x - reg->bx, reg->y - reg->by, z)); + // Set to this value + (*this)(reg->x, reg->y, z) = + ((2. * val) - (*this)(reg->x - reg->bx, reg->y - reg->by, z)); + } + } + } + } else if (newreg->isY) { + // nothing to do + } else { + auto* reg = newreg->getLegacyPointer(); + if (isFci() && reg->by != 0) { + continue; + } + /// Loop within each region + for (reg->first(); !reg->isDone(); reg->next()) { + for (int z = 0; z < nz; z++) { + // Get value half-way between cells + const BoutReal val = + 0.5 * (f3d(reg->x, reg->y, z) + f3d(reg->x - reg->bx, reg->y - reg->by, z)); + // Set to this value + (*this)(reg->x, reg->y, z) = + (2. * val) - (*this)(reg->x - reg->bx, reg->y - reg->by, z); + } } } } @@ -475,10 +575,13 @@ void Field3D::setBoundaryTo(const Field3D& f3d) { void Field3D::applyParallelBoundary() { - TRACE("Field3D::applyParallelBoundary()"); - checkData(*this); - ASSERT1(hasParallelSlices()); + if (isFci()) { + ASSERT1(hasParallelSlices()); + } + if (!hasParallelSlices()) { + return; + } // Apply boundary to this field for (const auto& bndry : getBoundaryOpPars()) { @@ -488,10 +591,13 @@ void Field3D::applyParallelBoundary() { void Field3D::applyParallelBoundary(BoutReal t) { - TRACE("Field3D::applyParallelBoundary(t)"); - checkData(*this); - ASSERT1(hasParallelSlices()); + if (isFci()) { + ASSERT1(hasParallelSlices()); + } + if (!hasParallelSlices()) { + return; + } // Apply boundary to this field for (const auto& bndry : getBoundaryOpPars()) { @@ -501,10 +607,13 @@ void Field3D::applyParallelBoundary(BoutReal t) { void Field3D::applyParallelBoundary(const std::string& condition) { - TRACE("Field3D::applyParallelBoundary(condition)"); - checkData(*this); - ASSERT1(hasParallelSlices()); + if (isFci()) { + ASSERT1(hasParallelSlices()); + } + if (!hasParallelSlices()) { + return; + } /// Get the boundary factory (singleton) BoundaryFactory* bfact = BoundaryFactory::getInstance(); @@ -520,10 +629,13 @@ void Field3D::applyParallelBoundary(const std::string& condition) { void Field3D::applyParallelBoundary(const std::string& region, const std::string& condition) { - TRACE("Field3D::applyParallelBoundary(region, condition)"); - checkData(*this); - ASSERT1(hasParallelSlices()); + if (isFci()) { + ASSERT1(hasParallelSlices()); + } + if (!hasParallelSlices()) { + return; + } /// Get the boundary factory (singleton) BoundaryFactory* bfact = BoundaryFactory::getInstance(); @@ -542,10 +654,13 @@ void Field3D::applyParallelBoundary(const std::string& region, void Field3D::applyParallelBoundary(const std::string& region, const std::string& condition, Field3D* f) { - TRACE("Field3D::applyParallelBoundary(region, condition, f)"); - checkData(*this); - ASSERT1(hasParallelSlices()); + if (isFci()) { + ASSERT1(hasParallelSlices()); + } + if (!hasParallelSlices()) { + return; + } /// Get the boundary factory (singleton) BoundaryFactory* bfact = BoundaryFactory::getInstance(); @@ -565,16 +680,16 @@ void Field3D::applyParallelBoundary(const std::string& region, } } +void Field3D::swapData(Field3D& other) { std::swap(data, other.data); } + /*************************************************************** * NON-MEMBER OVERLOADED OPERATORS ***************************************************************/ -Field3D operator-(const Field3D& f) { return -1.0 * f; } - //////////////// NON-MEMBER FUNCTIONS ////////////////// Field3D pow(const Field3D& lhs, const Field2D& rhs, const std::string& rgn) { - TRACE("pow(Field3D, Field2D)"); + // Check if the inputs are allocated checkData(lhs); checkData(rhs); @@ -590,7 +705,6 @@ Field3D pow(const Field3D& lhs, const Field2D& rhs, const std::string& rgn) { } FieldPerp pow(const Field3D& lhs, const FieldPerp& rhs, const std::string& rgn) { - TRACE("pow(Field3D, FieldPerp)"); checkData(lhs); checkData(rhs); @@ -610,8 +724,8 @@ FieldPerp pow(const Field3D& lhs, const FieldPerp& rhs, const std::string& rgn) // Friend functions Field3D filter(const Field3D& var, int N0, const std::string& rgn) { - TRACE("filter(Field3D, int)"); + bout::fft::assertZSerial(*var.getMesh(), "`filter`"); checkData(var); int ncz = var.getNz(); @@ -656,8 +770,8 @@ Field3D filter(const Field3D& var, int N0, const std::string& rgn) { // Fourier filter in z with zmin Field3D lowPass(const Field3D& var, int zmax, bool keep_zonal, const std::string& rgn) { - TRACE("lowPass(Field3D, {}, {})", zmax, keep_zonal); + bout::fft::assertZSerial(*var.getMesh(), "`lowPass`"); checkData(var); int ncz = var.getNz(); @@ -702,11 +816,11 @@ Field3D lowPass(const Field3D& var, int zmax, bool keep_zonal, const std::string return result; } -/* +/* * Use FFT to shift by an angle in the Z direction */ void shiftZ(Field3D& var, int jx, int jy, double zangle) { - TRACE("shiftZ"); + bout::fft::assertZSerial(*var.getMesh(), "`shiftZ`"); checkData(var); var.allocate(); // Ensure that var is unique Mesh* localmesh = var.getMesh(); @@ -775,7 +889,6 @@ void checkData(const Field3D& f, const std::string& region) { #endif Field2D DC(const Field3D& f, const std::string& rgn) { - TRACE("DC(Field3D)"); checkData(f); @@ -805,7 +918,8 @@ bool operator==(const Field3D& a, const Field3D& b) { if (!a.isAllocated() || !b.isAllocated()) { return false; } - return min(abs(a - b)) < 1e-10; + Field3D Sub = a - b; + return min(Sub) < 1e-10; } std::ostream& operator<<(std::ostream& out, const Field3D& value) { @@ -813,6 +927,19 @@ std::ostream& operator<<(std::ostream& out, const Field3D& value) { return out; } +namespace bout::detail { + +const Region& getField3DRegion(const Mesh* mesh, std::optional regionID) { + ASSERT1(mesh != nullptr); + + if (regionID.has_value()) { + return mesh->getRegion(regionID.value()); + } + return mesh->getRegion("RGN_ALL"); +} + +} // namespace bout::detail + void swap(Field3D& first, Field3D& second) noexcept { using std::swap; @@ -839,3 +966,105 @@ Field3D::getValidRegionWithDefault(const std::string& region_name) const { void Field3D::setRegion(const std::string& region_name) { regionID = fieldmesh->getRegionID(region_name); } + +void Field3D::resetRegionParallel(const bool force) { + if (force or isFci()) { + for (int i = 0; i < fieldmesh->ystart; ++i) { + yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", i + 1)); + ydown_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", -i - 1)); + } + } +} + +Field3D& Field3D::enableTracking(const std::string& name, + std::weak_ptr _tracking) { + tracking = std::move(_tracking); + tracking_state = 1; + selfname = name; + return *this; +} + +template +void Field3D::_track(const T& change, std::string operation) { + if (tracking_state == 0) { + return; + } + auto locked = tracking.lock(); + if (locked == nullptr) { + return; + } + const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; + + locked->set(outname, change, "tracking"); + + const std::string trace = cpptrace::generate_trace().to_string(); + + // Workaround for bug in gcc9.4 +#if BOUT_USE_TRACK + const std::string changename = change.name; +#endif + (*locked)[outname].setAttributes({ + {"operation", operation}, +#if BOUT_USE_TRACK + {"rhs.name", changename}, +#endif + {"trace", trace}, + }); +} + +template void +Field3D::_track>(const Field3D&, + std::string); +template void Field3D::_track(const Field3DParallel&, std::string); +template void Field3D::_track(const Field2D&, std::string); +template void Field3D::_track<>(const FieldPerp&, std::string); + +void Field3D::_track(const BoutReal& change, std::string operation) { + if (tracking_state == 0) { + return; + } + auto locked = tracking.lock(); + if (locked == nullptr) { + return; + } + const std::string trace = cpptrace::generate_trace().to_string(); + const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; + locked->set(outname, change, "tracking"); + (*locked)[outname].setAttributes({ + {"operation", operation}, + {"rhs.name", "BoutReal"}, + {"trace", trace}, + }); +} + +void Field3DParallel::ensureFieldAligned() { + if (isFci()) { + ASSERT2(hasParallelSlices()); + if (fieldmesh != nullptr) { + for (int i = 0; i < fieldmesh->ystart; ++i) { + ASSERT2(yup_fields[i].getRegionID().has_value()); + ASSERT2(ydown_fields[i].getRegionID().has_value()); + } + } + if (isAllocated()) { + for (int i = 0; i < fieldmesh->ystart; ++i) { + ASSERT2(yup_fields[i].isAllocated()); + ASSERT2(ydown_fields[i].isAllocated()); + } + } + } +} + +Field3DParallel& Field3DParallel::allocate() { + Field3D::allocate(); + if (isFci()) { + ASSERT2(hasParallelSlices()); + if (fieldmesh != nullptr) { + for (int i = 0; i < fieldmesh->ystart; ++i) { + yup_fields[i].allocate(); + ydown_fields[i].allocate(); + } + } + } + return *this; +} diff --git a/src/field/field_data.cxx b/src/field/field_data.cxx index b95c0462d9..b925cb1426 100644 --- a/src/field/field_data.cxx +++ b/src/field/field_data.cxx @@ -15,7 +15,6 @@ namespace bout { /// Throws if checks are enabled and trying to use a staggered /// location on a non-staggered mesh CELL_LOC normaliseLocation(CELL_LOC location, Mesh* mesh) { - AUTO_TRACE(); // CELL_DEFAULT always means CELL_CENTRE if (location == CELL_DEFAULT) { @@ -216,7 +215,6 @@ Mesh* FieldData::getMesh() const { } FieldData& FieldData::setLocation(CELL_LOC new_location) { - AUTO_TRACE(); location = bout::normaliseLocation(new_location, getMesh()); @@ -228,10 +226,7 @@ FieldData& FieldData::setLocation(CELL_LOC new_location) { return *this; } -CELL_LOC FieldData::getLocation() const { - AUTO_TRACE(); - return location; -} +CELL_LOC FieldData::getLocation() const { return location; } Coordinates* FieldData::getCoordinates() const { auto fieldCoordinates_shared = fieldCoordinates.lock(); diff --git a/src/field/field_factory.cxx b/src/field/field_factory.cxx index f65f2e7f55..188a64cf0c 100644 --- a/src/field/field_factory.cxx +++ b/src/field/field_factory.cxx @@ -1,7 +1,7 @@ /************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010-2025 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -176,6 +176,9 @@ FieldFactory::FieldFactory(Mesh* localmesh, Options* opt) // Where switch function addGenerator("where", std::make_shared(nullptr, nullptr, nullptr)); + + // Periodic in the Y direction? + addGenerator("is_periodic_y", std::make_shared()); } Field2D FieldFactory::create2D(const std::string& value, const Options* opt, @@ -185,7 +188,6 @@ Field2D FieldFactory::create2D(const std::string& value, const Options* opt, Field2D FieldFactory::create2D(FieldGeneratorPtr gen, Mesh* localmesh, CELL_LOC loc, BoutReal t) const { - AUTO_TRACE(); if (localmesh == nullptr) { if (fieldmesh == nullptr) { @@ -217,7 +219,6 @@ Field3D FieldFactory::create3D(const std::string& value, const Options* opt, Field3D FieldFactory::create3D(FieldGeneratorPtr gen, Mesh* localmesh, CELL_LOC loc, BoutReal t) const { - AUTO_TRACE(); if (localmesh == nullptr) { if (fieldmesh == nullptr) { @@ -268,7 +269,6 @@ FieldPerp FieldFactory::createPerp(const std::string& value, const Options* opt, FieldPerp FieldFactory::createPerp(FieldGeneratorPtr gen, Mesh* localmesh, CELL_LOC loc, BoutReal t) const { - AUTO_TRACE(); if (localmesh == nullptr) { if (fieldmesh == nullptr) { diff --git a/src/field/fieldgenerators.cxx b/src/field/fieldgenerators.cxx index 1f21f5c262..7e728664f8 100644 --- a/src/field/fieldgenerators.cxx +++ b/src/field/fieldgenerators.cxx @@ -1,6 +1,8 @@ #include "fieldgenerators.hxx" +#include + #include #include diff --git a/src/field/fieldgenerators.hxx b/src/field/fieldgenerators.hxx index 2485b4b82d..050e335448 100644 --- a/src/field/fieldgenerators.hxx +++ b/src/field/fieldgenerators.hxx @@ -352,4 +352,29 @@ private: FieldGeneratorPtr test, gt0, lt0; }; +/// Function that evaluates to 1 when Y is periodic (i.e. in the core), 0 otherwise +/// Note: Assumes symmetricGlobalX +class FieldPeriodicY : public FieldGenerator { +public: + FieldPeriodicY() = default; + FieldGeneratorPtr clone(const std::list UNUSED(args)) override { + return std::make_shared(); + } + BoutReal generate(const bout::generator::Context& ctx) override { + const Mesh* mesh = ctx.getMesh(); + const BoutReal local_inner_boundary = + 0.5 * (mesh->GlobalX(mesh->xstart - 1) + mesh->GlobalX(mesh->xstart)); + const BoutReal local_outer_boundary = + 0.5 * (mesh->GlobalX(mesh->xend + 1) + mesh->GlobalX(mesh->xend)); + const int local_index = mesh->xstart + + int(((ctx.x() - local_inner_boundary) + / (local_outer_boundary - local_inner_boundary)) + * (mesh->xend - mesh->xstart + 1)); + if (mesh->periodicY(local_index)) { + return 1.0; + } + return 0.0; + } +}; + #endif // BOUT_FIELDGENERATORS_H diff --git a/src/field/fieldperp.cxx b/src/field/fieldperp.cxx index ca9bdc0397..b7b2d9d731 100644 --- a/src/field/fieldperp.cxx +++ b/src/field/fieldperp.cxx @@ -2,10 +2,10 @@ * Class for 2D X-Z slices * ************************************************************************** - * Copyright 2010 - 2025 BOUT++ developers + * Copyright 2010 - 2026 BOUT++ developers * * Contact: Ben Dudson, dudson2@llnl.gov - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -23,19 +23,21 @@ * **************************************************************************/ +#include "bout/unused.hxx" #include #include #include +#include +#include #include #include #include -#include #include FieldPerp::FieldPerp(Mesh* localmesh, CELL_LOC location_in, int yindex_in, - DirectionTypes directions) + DirectionTypes directions, std::optional UNUSED(regionID)) : Field(localmesh, location_in, directions), yindex(yindex_in) { if (fieldmesh) { nx = fieldmesh->LocalNx; @@ -51,7 +53,6 @@ FieldPerp::FieldPerp(Array data_in, Mesh* localmesh, CELL_LOC location int yindex_in, DirectionTypes directions) : Field(localmesh, location_in, directions), yindex(yindex_in), nx(fieldmesh->LocalNx), nz(fieldmesh->LocalNz), data(std::move(data_in)) { - TRACE("FieldPerp: Copy constructor from Array and Mesh"); ASSERT1(data.size() == nx * nz); } @@ -77,7 +78,7 @@ FieldPerp& FieldPerp::allocate() { } /*************************************************************** - * ASSIGNMENT + * ASSIGNMENT ***************************************************************/ FieldPerp& FieldPerp::operator=(const FieldPerp& rhs) { @@ -97,7 +98,6 @@ FieldPerp& FieldPerp::operator=(const FieldPerp& rhs) { } FieldPerp& FieldPerp::operator=(const BoutReal rhs) { - TRACE("FieldPerp = BoutReal"); allocate(); @@ -150,9 +150,6 @@ FieldPerp fromFieldAligned(const FieldPerp& f, const std::string& region) { ////////////// NON-MEMBER OVERLOADED OPERATORS ////////////// -// Unary minus -FieldPerp operator-(const FieldPerp& f) { return -1.0 * f; } - ///////////////////////////////////////////////// // functions diff --git a/src/field/gen_fieldops.jinja b/src/field/gen_fieldops.jinja index ecd4e628cc..913acadf7b 100644 --- a/src/field/gen_fieldops.jinja +++ b/src/field/gen_fieldops.jinja @@ -1,3 +1,6 @@ +{% set use_parallel_arg = lhs.field_type == "Field3DParallel" or rhs.field_type == "Field3DParallel" %} +{% set use_raja_path = region_loop == "BOUT_FOR_RAJA" and not use_parallel_arg %} + // Provide the C++ wrapper for {{operator_name}} of {{lhs}} and {{rhs}} {{out}} operator{{operator}}(const {{lhs.passByReference}}, const {{rhs.passByReference}}) { {% if lhs != "BoutReal" and rhs != "BoutReal" %} @@ -8,130 +11,362 @@ checkData({{lhs.name}}); checkData({{rhs.name}}); - {% if out == "Field3D" %} - {% if lhs == rhs == "Field3D" %} + {% if use_raja_path %} + {% if out.field_type == "FieldPerp" %} + auto {{out.name}}_acc = FieldPerpAccessor{ {{out.name}} }; + {% else %} + auto {{out.name}}_acc = FieldAccessor({{out.name}}); + {% endif %} + {% if lhs.field_type == "FieldPerp" %} + auto {{lhs.name}}_acc = FieldPerpAccessor{ {{lhs.name}} }; + {% elif lhs.field_type == "BoutReal" %} + {% else %} + auto {{lhs.name}}_acc = FieldAccessor({{lhs.name}}); + {% endif %} + {% if rhs.field_type == "FieldPerp" %} + auto {{rhs.name}}_acc = FieldPerpAccessor{ {{rhs.name}} }; + {% elif rhs.field_type == "BoutReal" %} + {% else %} + auto {{rhs.name}}_acc = FieldAccessor({{rhs.name}}); + {% endif %} + {% endif %} + + {% if out.region_type == "3D" %} + {% if lhs.region_type == rhs.region_type == "3D" %} {{out.name}}.setRegion({{lhs.name}}.getMesh()->getCommonRegion({{lhs.name}}.getRegionID(), {{rhs.name}}.getRegionID())); - {% elif lhs == "Field3D" %} + {% elif lhs.region_type == "3D" %} {{out.name}}.setRegion({{lhs.name}}.getRegionID()); - {% elif rhs == "Field3D" %} + {% elif rhs.region_type == "3D" %} {{out.name}}.setRegion({{rhs.name}}.getRegionID()); {% endif %} + {% if out == "Field3DParallel" %} + if ({{out.name}}.isFci()) { + {{ lhs.assertParallelSlices }} + {{ rhs.assertParallelSlices }} + {{out.name}}.splitParallelSlices(); + {% if lhs.region_type == "3D" %} + for (size_t i{0}; i < {{lhs.name}}.numberParallelSlices(); ++i) { + {% else %} + for (size_t i{0}; i < {{rhs.name}}.numberParallelSlices(); ++i) { + {% endif %} + {{out.name}}.yup(i) = {{lhs.yup}} {{operator}} {{rhs.yup}}; + {{out.name}}.ydown(i) = {{lhs.ydown}} {{operator}} {{rhs.ydown}}; + } + {{ out.assertParallelSlices }} + } + {% endif %} {% endif %} - {% if (out == "Field3D") and ((lhs == "Field2D") or (rhs =="Field2D")) %} + {% if (out == "Field3D") and ((lhs == "Field2D") or (rhs == "Field2D")) %} + {% if use_raja_path %} + int mesh_nz = {{lhs.name if lhs.field_type != "BoutReal" else rhs.name}}_acc.mesh_nz; + {% else %} Mesh *localmesh = {{lhs.name if lhs.field_type != "BoutReal" else rhs.name}}.getMesh(); + {% endif %} - {% if (lhs == "Field2D") %} + {% if lhs == "Field2D" %} {{region_loop}}({{index_var}}, {{lhs.name}}.getRegion({{region_name}})) { {% else %} {{region_loop}}({{index_var}}, {{rhs.name}}.getRegion({{region_name}})) { {% endif %} - const auto {{mixed_base_ind}} = localmesh->ind2Dto3D({{index_var}}); - {% if (operator == "/") and (rhs == "Field2D") %} - const auto tmp = 1.0 / {{rhs.mixed_index}}; - for (int {{jz_var}} = 0; {{jz_var}} < localmesh->LocalNz; ++{{jz_var}}){ - {{out.mixed_index}} = {{lhs.mixed_index}} * tmp; + {% if use_raja_path %} + const auto {{mixed_base_ind}} = {{index_var}} * mesh_nz; + {% else %} + const auto {{mixed_base_ind}} = localmesh->ind2Dto3D({{index_var}}); + {% endif %} + {% if (operator == "/") and (rhs == "Field2D") %} + {% if use_raja_path %} + const auto tmp = 1.0 / {{rhs.mixed_index_acc}}; + for (int {{jz_var}} = 0; {{jz_var}} < mesh_nz; ++{{jz_var}}) { + {{out.mixed_index_acc}} = {{lhs.mixed_index_acc}} * tmp; + {% else %} + const auto tmp = 1.0 / {{rhs.mixed_index}}; + for (int {{jz_var}} = 0; {{jz_var}} < localmesh->LocalNz; ++{{jz_var}}) { + {{out.mixed_index}} = {{lhs.mixed_index}} * tmp; + {% endif %} + {% else %} + {% if use_raja_path %} + for (int {{jz_var}} = 0; {{jz_var}} < mesh_nz; ++{{jz_var}}) { + {{out.mixed_index_acc}} = {{lhs.mixed_index_acc}} {{operator}} {{rhs.mixed_index_acc}}; {% else %} - for (int {{jz_var}} = 0; {{jz_var}} < localmesh->LocalNz; ++{{jz_var}}){ - {{out.mixed_index}} = {{lhs.mixed_index}} {{operator}} {{rhs.mixed_index}}; + for (int {{jz_var}} = 0; {{jz_var}} < localmesh->LocalNz; ++{{jz_var}}) { + {{out.mixed_index}} = {{lhs.mixed_index}} {{operator}} {{rhs.mixed_index}}; {% endif %} - } - } - {% elif out == "FieldPerp" and (lhs == "Field2D" or lhs == "Field3D" or rhs == "Field2D" or rhs == "Field3D")%} + {% endif %} + } + }{% if use_raja_path %};{% endif %} + {% elif out == "FieldPerp" and (lhs == "Field2D" or lhs == "Field3D" or rhs == "Field2D" or rhs == "Field3D") %} Mesh *localmesh = {{lhs.name if lhs.field_type != "BoutReal" else rhs.name}}.getMesh(); {{region_loop}}({{index_var}}, {{out.name}}.getRegion({{region_name}})) { - int yind = {{lhs.name if lhs == "FieldPerp" else rhs.name}}.getIndex(); - const auto {{mixed_base_ind}} = localmesh->indPerpto3D({{index_var}}, yind); - {% if lhs != "FieldPerp" %} - {{out.index}} = {{lhs.base_index}} {{operator}} {{rhs.index}}; - {% else %} - {{out.index}} = {{lhs.index}} {{operator}} {{rhs.base_index}}; - {% endif %} - } + {% if use_raja_path %} + int yind = {{lhs.name if lhs == "FieldPerp" else rhs.name}}_acc.getIndex(); + const auto {{mixed_base_ind}} = localmesh->flatIndPerpto3D({{index_var}}, {{out.name}}_acc.nz, yind); + {% if lhs != "FieldPerp" %} + {{out.index_acc}} = {{lhs.base_index_acc}} {{operator}} {{rhs.index_acc}}; + {% else %} + {{out.index_acc}} = {{lhs.index_acc}} {{operator}} {{rhs.base_index_acc}}; + {% endif %} + {% else %} + int yind = {{lhs.name if lhs == "FieldPerp" else rhs.name}}.getIndex(); + const auto {{mixed_base_ind}} = localmesh->indPerpto3D({{index_var}}, yind); + {% if lhs != "FieldPerp" %} + {{out.index}} = {{lhs.base_index}} {{operator}} {{rhs.index}}; + {% else %} + {{out.index}} = {{lhs.index}} {{operator}} {{rhs.base_index}}; + {% endif %} + {% endif %} + }{% if use_raja_path %};{% endif %} {% elif (operator == "/") and (rhs == "BoutReal") %} - const auto tmp = 1.0 / {{rhs.index}}; - {{region_loop}}({{index_var}}, {{out.name}}.getValidRegionWithDefault({{region_name}})) { - {{out.index}} = {{lhs.index}} * tmp; - } + const auto tmp = 1.0 / {{rhs.index}}; + {{region_loop}}({{index_var}}, {{out.name}}.getValidRegionWithDefault({{region_name}})) { + {% if use_raja_path %} + {{out.index_acc}} = {{lhs.index_acc}} * tmp; + {% else %} + {{out.index}} = {{lhs.index}} * tmp; + {% endif %} + }{% if use_raja_path %};{% endif %} {% else %} {{region_loop}}({{index_var}}, {{out.name}}.getValidRegionWithDefault({{region_name}})) { - {{out.index}} = {{lhs.index}} {{operator}} {{rhs.index}}; - } + {% if use_raja_path %} + {{out.index_acc}} = {{lhs.index_acc}} {{operator}} {{rhs.index_acc}}; + {% else %} + {{out.index}} = {{lhs.index}} {{operator}} {{rhs.index}}; + {% endif %} + }{% if use_raja_path %};{% endif %} {% endif %} checkData({{out.name}}); return {{out.name}}; } +{% if out.field_type == lhs.field_type and lhs == "Field3D" %} +// Provide the C++ operator to update {{lhs}} by {{operator_name}} with {{rhs}} +{{lhs}} &{{lhs}}::update_{{operator_name}}_inplace(const {{rhs.passByReference}}) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + {% if lhs != "BoutReal" and rhs != "BoutReal" %} + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + {% endif %} + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData({{rhs.name}}); + + {% if use_raja_path %} + auto this_acc = FieldAccessor(*this); + {% if rhs.field_type == "FieldPerp" %} + auto {{rhs.name}}_acc = FieldPerpAccessor{ {{rhs.name}} }; + {% elif rhs.field_type == "BoutReal" %} + {% else %} + auto {{rhs.name}}_acc = FieldAccessor({{rhs.name}}); + {% endif %} + {% endif %} + + {% if lhs.region_type == rhs.region_type == "3D" %} + regionID = fieldmesh->getCommonRegion(regionID, {{rhs.name}}.getRegionID()); + {% endif %} + + {% if rhs == "Field2D" %} + {% if use_raja_path %} + int mesh_nz = fieldmesh->LocalNz; + {% endif %} + {{region_loop}}({{index_var}}, {{rhs.name}}.getRegion({{region_name}})) { + {% if use_raja_path %} + const auto {{mixed_base_ind}} = {{index_var}} * mesh_nz; + {% else %} + const auto {{mixed_base_ind}} = fieldmesh->ind2Dto3D({{index_var}}); + {% endif %} + {% if operator == "/" %} + {% if use_raja_path %} + const auto tmp = 1.0 / {{rhs.mixed_index_acc}}; + for (int {{jz_var}} = 0; {{jz_var}} < mesh_nz; ++{{jz_var}}) { + this_acc[{{mixed_base_ind}} + {{jz_var}}] *= tmp; + {% else %} + const auto tmp = 1.0 / {{rhs.mixed_index}}; + for (int {{jz_var}} = 0; {{jz_var}} < fieldmesh->LocalNz; ++{{jz_var}}) { + (*this)[{{mixed_base_ind}} + {{jz_var}}] *= tmp; + {% endif %} + {% else %} + {% if use_raja_path %} + for (int {{jz_var}} = 0; {{jz_var}} < mesh_nz; ++{{jz_var}}) { + this_acc[{{mixed_base_ind}} + {{jz_var}}] {{operator}}= {{rhs.index_acc}}; + {% else %} + for (int {{jz_var}} = 0; {{jz_var}} < fieldmesh->LocalNz; ++{{jz_var}}) { + (*this)[{{mixed_base_ind}} + {{jz_var}}] {{operator}}= {{rhs.index}}; + {% endif %} + {% endif %} + } + }{% if use_raja_path %};{% endif %} + {% elif (operator == "/") and (rhs == "BoutReal") %} + const auto tmp = 1.0 / {{rhs.index}}; + {{region_loop}}({{index_var}}, this->getRegion({{region_name}})) { + {% if use_raja_path %} + this_acc[{{index_var}}] *= tmp; + {% else %} + (*this)[{{index_var}}] *= tmp; + {% endif %} + }{% if use_raja_path %};{% endif %} + {% else %} + {{region_loop}}({{index_var}}, this->getRegion({{region_name}})) { + {% if use_raja_path %} + this_acc[{{index_var}}] {{operator}}= {{rhs.index_acc}}; + {% else %} + (*this)[{{index_var}}] {{operator}}= {{rhs.index}}; + {% endif %} + }{% if use_raja_path %};{% endif %} + {% endif %} + + {% if lhs.region_type == "3D" %} + track(rhs, "operator{{operator}}="); + {% endif %} +#if BOUT_USE_TRACK + name = fmt::format("{:s} {{operator}}= {:s}", this->name, {{'"BR"' if rhs == "BoutReal" else rhs.name + ".name"}}); +#endif + + checkData(*this); + + return *this; +} +{% endif %} + + {% if out.field_type == lhs.field_type %} // Provide the C++ operator to update {{lhs}} by {{operator_name}} with {{rhs}} {{lhs}} &{{lhs}}::operator{{operator}}=(const {{rhs.passByReference}}) { // only if data is unique we update the field // otherwise just call the non-inplace version +{% if lhs == "Field3DParallel" %} + if (data.unique() or isRef) { +{% else %} if (data.unique()) { +{% endif %} {% if lhs != "BoutReal" and rhs != "BoutReal" %} ASSERT1_FIELDS_COMPATIBLE(*this, rhs); {% endif %} - {% if (lhs == "Field3D") %} - // Delete existing parallel slices. We don't copy parallel slices, so any + {% if lhs == "Field3D" %} + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - + {% endif %} + {% if lhs == "Field3DParallel" and (rhs.region_type == "3D" or rhs == "BoutReal") %} + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_{{operator_name}}_inplace({{rhs.name}}{% if rhs == "Field3D" %}.yup(i){% endif %}); + ydown(i).update_{{operator_name}}_inplace({{rhs.name}}{% if rhs == "Field3D" %}.ydown(i){% endif %}); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) {{operator}}= {{rhs.name}}{% if rhs == "Field3D" %}.yup(i){% endif %}; + ydown(i) {{operator}}= {{rhs.name}}{% if rhs == "Field3D" %}.ydown(i){% endif %}; + } + } + } else { + clearParallelSlices(); + } {% endif %} checkData(*this); checkData({{rhs.name}}); - {% if lhs == rhs == "Field3D" %} - regionID = fieldmesh->getCommonRegion(regionID, {{rhs.name}}.regionID); + {% if use_raja_path %} + {% if lhs.field_type == "FieldPerp" %} + auto this_acc = FieldPerpAccessor{(*this)}; + {% else %} + auto this_acc = FieldAccessor(*this); + {% endif %} + {% if rhs.field_type == "FieldPerp" %} + auto {{rhs.name}}_acc = FieldPerpAccessor{ {{rhs.name}} }; + {% elif rhs.field_type == "BoutReal" %} + {% else %} + auto {{rhs.name}}_acc = FieldAccessor({{rhs.name}}); + {% endif %} {% endif %} + {% if lhs.region_type == rhs.region_type == "3D" %} + regionID = fieldmesh->getCommonRegion(regionID, {{rhs.name}}.getRegionID()); + {% endif %} - {% if (lhs == "Field3D") and (rhs =="Field2D") %} + {% if (lhs == "Field3D") and (rhs == "Field2D") %} + {% if use_raja_path %} + int mesh_nz = fieldmesh->LocalNz; + {% endif %} {{region_loop}}({{index_var}}, {{rhs.name}}.getRegion({{region_name}})) { - const auto {{mixed_base_ind}} = fieldmesh->ind2Dto3D({{index_var}}); - {% if (operator == "/") and (rhs == "Field2D") %} - const auto tmp = 1.0 / {{rhs.mixed_index}}; - for (int {{jz_var}} = 0; {{jz_var}} < fieldmesh->LocalNz; ++{{jz_var}}){ - (*this)[{{mixed_base_ind}} + {{jz_var}}] *= tmp; + {% if use_raja_path %} + const auto {{mixed_base_ind}} = {{index_var}} * mesh_nz; + {% else %} + const auto {{mixed_base_ind}} = fieldmesh->ind2Dto3D({{index_var}}); + {% endif %} + {% if operator == "/" %} + {% if use_raja_path %} + const auto tmp = 1.0 / {{rhs.mixed_index_acc}}; + for (int {{jz_var}} = 0; {{jz_var}} < mesh_nz; ++{{jz_var}}) { + this_acc[{{mixed_base_ind}} + {{jz_var}}] *= tmp; {% else %} - for (int {{jz_var}} = 0; {{jz_var}} < fieldmesh->LocalNz; ++{{jz_var}}){ - (*this)[{{mixed_base_ind}} + {{jz_var}}] {{operator}}= {{rhs.index}}; + const auto tmp = 1.0 / {{rhs.mixed_index}}; + for (int {{jz_var}} = 0; {{jz_var}} < fieldmesh->LocalNz; ++{{jz_var}}) { + (*this)[{{mixed_base_ind}} + {{jz_var}}] *= tmp; {% endif %} - } - } - {% elif lhs == "FieldPerp" and (rhs == "Field3D" or rhs == "Field2D")%} + {% else %} + {% if use_raja_path %} + for (int {{jz_var}} = 0; {{jz_var}} < mesh_nz; ++{{jz_var}}) { + this_acc[{{mixed_base_ind}} + {{jz_var}}] {{operator}}= {{rhs.index_acc}}; + {% else %} + for (int {{jz_var}} = 0; {{jz_var}} < fieldmesh->LocalNz; ++{{jz_var}}) { + (*this)[{{mixed_base_ind}} + {{jz_var}}] {{operator}}= {{rhs.index}}; + {% endif %} + {% endif %} + } + }{% if use_raja_path %};{% endif %} + {% elif lhs == "FieldPerp" and (rhs == "Field3D" or rhs == "Field2D") %} Mesh *localmesh = this->getMesh(); + {% if use_raja_path %} + int yind = this_acc.getIndex(); + {% endif %} {{region_loop}}({{index_var}}, this->getRegion({{region_name}})) { - int yind = this->getIndex(); - const auto {{mixed_base_ind}} = localmesh->indPerpto3D({{index_var}}, yind); - (*this)[{{index_var}}] {{operator}}= {{rhs.base_index}}; - } - {% elif rhs == "FieldPerp" and (lhs == "Field3D" or lhs == "Field2D")%} - Mesh *localmesh = this->getMesh(); - - {{region_loop}}({{index_var}}, {{rhs.name}}.getRegion({{region_name}})) { - int yind = {{rhs.name}}.getIndex(); - const auto {{mixed_base_ind}} = localmesh->indPerpto3D({{index_var}}, yind); - (*this)[{{base_ind_var}}] {{operator}}= {{rhs.index}}; - } - {% elif (operator == "/") and (lhs == "Field3D" or lhs == "Field2D") and (rhs =="BoutReal") %} + {% if use_raja_path %} + const auto {{mixed_base_ind}} = localmesh->flatIndPerpto3D({{index_var}}, this_acc.nz, yind); + this_acc[{{index_var}}] {{operator}}= {{rhs.base_index_acc}}; + {% else %} + int yind = this->getIndex(); + const auto {{mixed_base_ind}} = localmesh->indPerpto3D({{index_var}}, yind); + (*this)[{{index_var}}] {{operator}}= {{rhs.base_index}}; + {% endif %} + }{% if use_raja_path %};{% endif %} + {% elif (operator == "/") and (lhs == "Field3D" or lhs == "Field2D") and (rhs == "BoutReal") %} const auto tmp = 1.0 / {{rhs.index}}; {{region_loop}}({{index_var}}, this->getRegion({{region_name}})) { + {% if use_raja_path %} + this_acc[{{index_var}}] *= tmp; + {% else %} (*this)[{{index_var}}] *= tmp; - } + {% endif %} + }{% if use_raja_path %};{% endif %} {% else %} {{region_loop}}({{index_var}}, this->getRegion({{region_name}})) { - (*this)[{{index_var}}] {{operator}}= {{rhs.index}}; - } + {% if use_raja_path %} + this_acc[{{index_var}}] {{operator}}= {{rhs.index_acc}}; + {% else %} + (*this)[{{index_var}}] {{operator}}= {{rhs.index}}; + {% endif %} + }{% if use_raja_path %};{% endif %} + {% endif %} + + {% if lhs.region_type == "3D" %} + track(rhs, "operator{{operator}}="); {% endif %} checkData(*this); } else { + {% if lhs.region_type == "3D" %} + track(rhs, "operator{{operator}}="); + {% endif %} (*this) = (*this) {{operator}} {{rhs.name}}; } return *this; diff --git a/src/field/gen_fieldops.py b/src/field/gen_fieldops.py index 29631ff7aa..6610286af5 100755 --- a/src/field/gen_fieldops.py +++ b/src/field/gen_fieldops.py @@ -11,17 +11,14 @@ """ - from __future__ import print_function -from builtins import object - import argparse -from collections import OrderedDict import contextlib -from copy import deepcopy as copy import itertools import sys +from builtins import object +from copy import deepcopy as copy try: import jinja2 @@ -54,17 +51,18 @@ def smart_open(filename, mode="r"): # The arthimetic operators -# OrderedDict to (try to) ensure consistency between python 2 & 3 -operators = OrderedDict( - [ - ("*", "multiplication"), - ("/", "division"), - ("+", "addition"), - ("-", "subtraction"), - ] -) +operators = { + "*": "multiplication", + "/": "division", + "+": "addition", + "-": "subtraction", +} + header = """// This file is autogenerated - see gen_fieldops.py +#include "fmt/format.h" +#include "bout/assert.hxx" +#include "bout/build_defines.hxx" #include #include #include @@ -104,7 +102,7 @@ def __init__( self.mixed_base_ind_var = mixed_base_ind_var # Note region_type isn't actually used currently but # may be useful in future. - if self.field_type == "Field3D": + if "Field3D" in self.field_type: self.region_type = "3D" elif self.field_type == "Field2D": self.region_type = "2D" @@ -132,6 +130,17 @@ def index(self): else: return "{self.name}[{self.index_var}]".format(self=self) + @property + def index_acc(self): + """Returns "_acc[{index_var}]" for an accessor-based index, except if + field_type is BoutReal, in which case just returns "" + + """ + if self.field_type == "BoutReal": + return "{self.name}".format(self=self) + else: + return "{self.name}_acc[{self.index_var}]".format(self=self) + @property def mixed_index(self): """Returns "[{index_var} + {jz_var}]" if field_type is Field3D, @@ -147,6 +156,21 @@ def mixed_index(self): else: # Field2D return "{self.name}[{self.index_var}]".format(self=self) + @property + def mixed_index_acc(self): + """Returns "_acc[{index_var} + {jz_var}]" for an accessor if field_type + is Field3D, self.index if Field2D or just returns "" for BoutReal + + """ + if self.field_type == "BoutReal": + return "{self.name}_acc".format(self=self) + elif self.field_type == "Field3D": + return "{self.name}_acc[{self.mixed_base_ind_var} + {self.jz_var}]".format( + self=self + ) + else: # Field2D + return "{self.name}_acc[{self.index_var}]".format(self=self) + @property def base_index(self): """Returns "[{mixed_base_ind_var}]" if field_type is Field3D, Field2D or FieldPerp @@ -158,6 +182,46 @@ def base_index(self): else: return "{self.name}[{self.mixed_base_ind_var}]".format(self=self) + @property + def base_index_acc(self): + """Returns "_acc[{mixed_base_ind_var}]" for an accessor if field_type is + Field3D, Field2D or FieldPerp or just returns "" for BoutReal + + """ + if self.field_type == "BoutReal": + return "{self.name}".format(self=self) + else: + return "{self.name}_acc[{self.mixed_base_ind_var}]".format(self=self) + + @property + def yup(self): + """Returns {{name}}.yup(i) if it is a field with parallel slices. + If it is BoutReal just {{name}}""" + if self.field_type == "BoutReal": + return "{self.name}".format(self=self) + return "{self.name}.yup(i)".format(self=self) + + @property + def ydown(self): + """Returns {{name}}.ydown(i) if it is a field with parallel slices. + If it is BoutReal just {{name}}""" + if self.field_type == "BoutReal": + return "{self.name}".format(self=self) + return "{self.name}.ydown(i)".format(self=self) + + @property + def assertParallelSlices(self): + if self.field_type == "BoutReal": + return "" + + return f""" + ASSERT2({self.name}.hasParallelSlices()); + for (size_t i{{0}} ; i < {self.name}.numberParallelSlices() ; ++i) {{ + ASSERT2({self.ydown}.isAllocated()); + ASSERT2({self.yup}.isAllocated()); + }}\ + """ + def __eq__(self, other): try: return self.field_type == other.field_type @@ -184,6 +248,8 @@ def returnType(f1, f2): return copy(f1) elif f1 == "FieldPerp" or f2 == "FieldPerp": return copy(fieldPerp) + elif f1 == "Field3DParallel" or f2 == "Field3DParallel": + return copy(field3DPar) else: return copy(field3D) @@ -198,11 +264,11 @@ def returnType(f1, f2): ) # By default use OpenMP enabled loops but allow to disable parser.add_argument( - "--no-openmp", - action="store_false", - default=False, - dest="noOpenMP", - help="Don't use OpenMP compatible loops", + "--loop-exec", + default="openmp", + dest="loop_exec", + choices=["serial", "openmp", "raja"], + help="Choose the loop execution method. Default is OpenMP", ) args = parser.parse_args() @@ -213,13 +279,18 @@ def returnType(f1, f2): mixed_base_ind_var = "base_ind" region_name = '"RGN_ALL"' - if args.noOpenMP: + if args.loop_exec == "openmp": + region_loop = "BOUT_FOR" + elif args.loop_exec == "raja": + region_loop = "BOUT_FOR_RAJA" + header += "#include \n" + header += "#include \n" + elif args.loop_exec == "serial": region_loop = "BOUT_FOR_SERIAL" else: - region_loop = "BOUT_FOR" + raise ValueError("Unknown loop execution method") # Declare what fields we currently support: - # Field perp is currently missing field3D = Field( "Field3D", ["x", "y", "z"], @@ -227,6 +298,13 @@ def returnType(f1, f2): jz_var=jz_var, mixed_base_ind_var=mixed_base_ind_var, ) + field3DPar = Field( + "Field3DParallel", + ["x", "y", "z"], + index_var=index_var, + jz_var=jz_var, + mixed_base_ind_var=mixed_base_ind_var, + ) field2D = Field( "Field2D", ["x", "y"], @@ -249,7 +327,8 @@ def returnType(f1, f2): mixed_base_ind_var=mixed_base_ind_var, ) - fields = [field3D, field2D, fieldPerp, boutreal] + fields = (field3D, field2D, fieldPerp, boutreal) + fields2 = (field3D, field3DPar, boutreal) with smart_open(args.filename, "w") as f: f.write(header) @@ -259,10 +338,16 @@ def returnType(f1, f2): template = env.get_template("gen_fieldops.jinja") - for lhs, rhs in itertools.product(fields, fields): - # We don't have to define BoutReal BoutReal operations - if lhs == rhs == "BoutReal": + # We don't have to define BoutReal BoutReal operations + done = [(boutreal, boutreal)] + for lhs, rhs in itertools.chain( + itertools.product(fields, fields), + itertools.product(fields2, fields2), + ): + if (lhs, rhs) in done: continue + done.append((lhs, rhs)) + rhs = copy(rhs) lhs = copy(lhs) diff --git a/src/field/generated_fieldops.cxx b/src/field/generated_fieldops.cxx index 6b778acee3..d47b2a7a89 100644 --- a/src/field/generated_fieldops.cxx +++ b/src/field/generated_fieldops.cxx @@ -1,4 +1,7 @@ // This file is autogenerated - see gen_fieldops.py +#include "fmt/format.h" +#include "bout/assert.hxx" +#include "bout/build_defines.hxx" #include #include #include @@ -16,14 +19,38 @@ Field3D operator*(const Field3D& lhs, const Field3D& rhs) { result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] * rhs[index]; } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by multiplication with Field3D +Field3D& Field3D::update_multiplication_inplace(const Field3D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } + track(rhs, "operator*="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, rhs.name); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by multiplication with Field3D Field3D& Field3D::operator*=(const Field3D& rhs) { // only if data is unique we update the field @@ -31,20 +58,21 @@ Field3D& Field3D::operator*=(const Field3D& rhs) { if (data.unique()) { ASSERT1_FIELDS_COMPATIBLE(*this, rhs); - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - regionID = fieldmesh->getCommonRegion(regionID, rhs.regionID); + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } + track(rhs, "operator*="); checkData(*this); } else { + track(rhs, "operator*="); (*this) = (*this) * rhs; } return *this; @@ -60,14 +88,38 @@ Field3D operator/(const Field3D& lhs, const Field3D& rhs) { result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] / rhs[index]; } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by division with Field3D +Field3D& Field3D::update_division_inplace(const Field3D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } + track(rhs, "operator/="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, rhs.name); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by division with Field3D Field3D& Field3D::operator/=(const Field3D& rhs) { // only if data is unique we update the field @@ -75,20 +127,21 @@ Field3D& Field3D::operator/=(const Field3D& rhs) { if (data.unique()) { ASSERT1_FIELDS_COMPATIBLE(*this, rhs); - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - regionID = fieldmesh->getCommonRegion(regionID, rhs.regionID); + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } + track(rhs, "operator/="); checkData(*this); } else { + track(rhs, "operator/="); (*this) = (*this) / rhs; } return *this; @@ -104,14 +157,38 @@ Field3D operator+(const Field3D& lhs, const Field3D& rhs) { result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] + rhs[index]; } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by addition with Field3D +Field3D& Field3D::update_addition_inplace(const Field3D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } + track(rhs, "operator+="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, rhs.name); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by addition with Field3D Field3D& Field3D::operator+=(const Field3D& rhs) { // only if data is unique we update the field @@ -119,20 +196,21 @@ Field3D& Field3D::operator+=(const Field3D& rhs) { if (data.unique()) { ASSERT1_FIELDS_COMPATIBLE(*this, rhs); - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - regionID = fieldmesh->getCommonRegion(regionID, rhs.regionID); + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } + track(rhs, "operator+="); checkData(*this); } else { + track(rhs, "operator+="); (*this) = (*this) + rhs; } return *this; @@ -148,14 +226,38 @@ Field3D operator-(const Field3D& lhs, const Field3D& rhs) { result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] - rhs[index]; } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by subtraction with Field3D +Field3D& Field3D::update_subtraction_inplace(const Field3D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } + track(rhs, "operator-="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, rhs.name); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by subtraction with Field3D Field3D& Field3D::operator-=(const Field3D& rhs) { // only if data is unique we update the field @@ -163,20 +265,21 @@ Field3D& Field3D::operator-=(const Field3D& rhs) { if (data.unique()) { ASSERT1_FIELDS_COMPATIBLE(*this, rhs); - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - regionID = fieldmesh->getCommonRegion(regionID, rhs.regionID); + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } + track(rhs, "operator-="); checkData(*this); } else { + track(rhs, "operator-="); (*this) = (*this) - rhs; } return *this; @@ -194,17 +297,44 @@ Field3D operator*(const Field3D& lhs, const Field2D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, rhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { const auto base_ind = localmesh->ind2Dto3D(index); for (int jz = 0; jz < localmesh->LocalNz; ++jz) { result[base_ind + jz] = lhs[base_ind + jz] * rhs[index]; } } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by multiplication with Field2D +Field3D& Field3D::update_multiplication_inplace(const Field2D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { + const auto base_ind = fieldmesh->ind2Dto3D(index); + for (int jz = 0; jz < fieldmesh->LocalNz; ++jz) { + (*this)[base_ind + jz] *= rhs[index]; + } + } + track(rhs, "operator*="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, rhs.name); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by multiplication with Field2D Field3D& Field3D::operator*=(const Field2D& rhs) { // only if data is unique we update the field @@ -212,23 +342,24 @@ Field3D& Field3D::operator*=(const Field2D& rhs) { if (data.unique()) { ASSERT1_FIELDS_COMPATIBLE(*this, rhs); - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - BOUT_FOR(index, rhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { const auto base_ind = fieldmesh->ind2Dto3D(index); for (int jz = 0; jz < fieldmesh->LocalNz; ++jz) { (*this)[base_ind + jz] *= rhs[index]; } } + track(rhs, "operator*="); checkData(*this); } else { + track(rhs, "operator*="); (*this) = (*this) * rhs; } return *this; @@ -246,18 +377,46 @@ Field3D operator/(const Field3D& lhs, const Field2D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, rhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { const auto base_ind = localmesh->ind2Dto3D(index); const auto tmp = 1.0 / rhs[index]; for (int jz = 0; jz < localmesh->LocalNz; ++jz) { result[base_ind + jz] = lhs[base_ind + jz] * tmp; } } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by division with Field2D +Field3D& Field3D::update_division_inplace(const Field2D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { + const auto base_ind = fieldmesh->ind2Dto3D(index); + const auto tmp = 1.0 / rhs[index]; + for (int jz = 0; jz < fieldmesh->LocalNz; ++jz) { + (*this)[base_ind + jz] *= tmp; + } + } + track(rhs, "operator/="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, rhs.name); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by division with Field2D Field3D& Field3D::operator/=(const Field2D& rhs) { // only if data is unique we update the field @@ -265,24 +424,25 @@ Field3D& Field3D::operator/=(const Field2D& rhs) { if (data.unique()) { ASSERT1_FIELDS_COMPATIBLE(*this, rhs); - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - BOUT_FOR(index, rhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { const auto base_ind = fieldmesh->ind2Dto3D(index); const auto tmp = 1.0 / rhs[index]; for (int jz = 0; jz < fieldmesh->LocalNz; ++jz) { (*this)[base_ind + jz] *= tmp; } } + track(rhs, "operator/="); checkData(*this); } else { + track(rhs, "operator/="); (*this) = (*this) / rhs; } return *this; @@ -300,17 +460,44 @@ Field3D operator+(const Field3D& lhs, const Field2D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, rhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { const auto base_ind = localmesh->ind2Dto3D(index); for (int jz = 0; jz < localmesh->LocalNz; ++jz) { result[base_ind + jz] = lhs[base_ind + jz] + rhs[index]; } } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by addition with Field2D +Field3D& Field3D::update_addition_inplace(const Field2D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { + const auto base_ind = fieldmesh->ind2Dto3D(index); + for (int jz = 0; jz < fieldmesh->LocalNz; ++jz) { + (*this)[base_ind + jz] += rhs[index]; + } + } + track(rhs, "operator+="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, rhs.name); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by addition with Field2D Field3D& Field3D::operator+=(const Field2D& rhs) { // only if data is unique we update the field @@ -318,23 +505,24 @@ Field3D& Field3D::operator+=(const Field2D& rhs) { if (data.unique()) { ASSERT1_FIELDS_COMPATIBLE(*this, rhs); - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - BOUT_FOR(index, rhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { const auto base_ind = fieldmesh->ind2Dto3D(index); for (int jz = 0; jz < fieldmesh->LocalNz; ++jz) { (*this)[base_ind + jz] += rhs[index]; } } + track(rhs, "operator+="); checkData(*this); } else { + track(rhs, "operator+="); (*this) = (*this) + rhs; } return *this; @@ -352,17 +540,44 @@ Field3D operator-(const Field3D& lhs, const Field2D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, rhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { const auto base_ind = localmesh->ind2Dto3D(index); for (int jz = 0; jz < localmesh->LocalNz; ++jz) { result[base_ind + jz] = lhs[base_ind + jz] - rhs[index]; } } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by subtraction with Field2D +Field3D& Field3D::update_subtraction_inplace(const Field2D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { + const auto base_ind = fieldmesh->ind2Dto3D(index); + for (int jz = 0; jz < fieldmesh->LocalNz; ++jz) { + (*this)[base_ind + jz] -= rhs[index]; + } + } + track(rhs, "operator-="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, rhs.name); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by subtraction with Field2D Field3D& Field3D::operator-=(const Field2D& rhs) { // only if data is unique we update the field @@ -370,23 +585,24 @@ Field3D& Field3D::operator-=(const Field2D& rhs) { if (data.unique()) { ASSERT1_FIELDS_COMPATIBLE(*this, rhs); - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - BOUT_FOR(index, rhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, rhs.getRegion("RGN_ALL")) { const auto base_ind = fieldmesh->ind2Dto3D(index); for (int jz = 0; jz < fieldmesh->LocalNz; ++jz) { (*this)[base_ind + jz] -= rhs[index]; } } + track(rhs, "operator-="); checkData(*this); } else { + track(rhs, "operator-="); (*this) = (*this) - rhs; } return *this; @@ -402,12 +618,11 @@ FieldPerp operator*(const Field3D& lhs, const FieldPerp& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = rhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[base_ind] * rhs[index]; } - checkData(result); return result; } @@ -422,12 +637,11 @@ FieldPerp operator/(const Field3D& lhs, const FieldPerp& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = rhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[base_ind] / rhs[index]; } - checkData(result); return result; } @@ -442,12 +656,11 @@ FieldPerp operator+(const Field3D& lhs, const FieldPerp& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = rhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[base_ind] + rhs[index]; } - checkData(result); return result; } @@ -462,12 +675,11 @@ FieldPerp operator-(const Field3D& lhs, const FieldPerp& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = rhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[base_ind] - rhs[index]; } - checkData(result); return result; } @@ -481,32 +693,54 @@ Field3D operator*(const Field3D& lhs, const BoutReal rhs) { result.setRegion(lhs.getRegionID()); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] * rhs; } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by multiplication with BoutReal +Field3D& Field3D::update_multiplication_inplace(const BoutReal rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } + track(rhs, "operator*="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, "BR"); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by multiplication with BoutReal Field3D& Field3D::operator*=(const BoutReal rhs) { // only if data is unique we update the field // otherwise just call the non-inplace version if (data.unique()) { - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } + track(rhs, "operator*="); checkData(*this); } else { + track(rhs, "operator*="); (*this) = (*this) * rhs; } return *this; @@ -522,33 +756,56 @@ Field3D operator/(const Field3D& lhs, const BoutReal rhs) { result.setRegion(lhs.getRegionID()); const auto tmp = 1.0 / rhs; - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] * tmp; } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by division with BoutReal +Field3D& Field3D::update_division_inplace(const BoutReal rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + const auto tmp = 1.0 / rhs; + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= tmp; } + track(rhs, "operator/="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, "BR"); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by division with BoutReal Field3D& Field3D::operator/=(const BoutReal rhs) { // only if data is unique we update the field // otherwise just call the non-inplace version if (data.unique()) { - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); const auto tmp = 1.0 / rhs; - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= tmp; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= tmp; } + track(rhs, "operator/="); checkData(*this); } else { + track(rhs, "operator/="); (*this) = (*this) / rhs; } return *this; @@ -563,32 +820,54 @@ Field3D operator+(const Field3D& lhs, const BoutReal rhs) { result.setRegion(lhs.getRegionID()); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] + rhs; } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by addition with BoutReal +Field3D& Field3D::update_addition_inplace(const BoutReal rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } + track(rhs, "operator+="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, "BR"); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by addition with BoutReal Field3D& Field3D::operator+=(const BoutReal rhs) { // only if data is unique we update the field // otherwise just call the non-inplace version if (data.unique()) { - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } + track(rhs, "operator+="); checkData(*this); } else { + track(rhs, "operator+="); (*this) = (*this) + rhs; } return *this; @@ -603,32 +882,54 @@ Field3D operator-(const Field3D& lhs, const BoutReal rhs) { result.setRegion(lhs.getRegionID()); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] - rhs; } - checkData(result); return result; } +// Provide the C++ operator to update Field3D by subtraction with BoutReal +Field3D& Field3D::update_subtraction_inplace(const BoutReal rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + + // Delete existing parallel slices. We don't update parallel slices, so any + // that currently exist will be incorrect. + clearParallelSlices(); + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } + track(rhs, "operator-="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, "BR"); +#endif + + checkData(*this); + + return *this; +} + // Provide the C++ operator to update Field3D by subtraction with BoutReal Field3D& Field3D::operator-=(const BoutReal rhs) { // only if data is unique we update the field // otherwise just call the non-inplace version if (data.unique()) { - // Delete existing parallel slices. We don't copy parallel slices, so any + // Delete existing parallel slices. We don't update parallel slices, so any // that currently exist will be incorrect. clearParallelSlices(); - checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } + track(rhs, "operator-="); checkData(*this); } else { + track(rhs, "operator-="); (*this) = (*this) - rhs; } return *this; @@ -646,13 +947,12 @@ Field3D operator*(const Field2D& lhs, const Field3D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, lhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, lhs.getRegion("RGN_ALL")) { const auto base_ind = localmesh->ind2Dto3D(index); for (int jz = 0; jz < localmesh->LocalNz; ++jz) { result[base_ind + jz] = lhs[index] * rhs[base_ind + jz]; } } - checkData(result); return result; } @@ -669,13 +969,12 @@ Field3D operator/(const Field2D& lhs, const Field3D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, lhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, lhs.getRegion("RGN_ALL")) { const auto base_ind = localmesh->ind2Dto3D(index); for (int jz = 0; jz < localmesh->LocalNz; ++jz) { result[base_ind + jz] = lhs[index] / rhs[base_ind + jz]; } } - checkData(result); return result; } @@ -692,13 +991,12 @@ Field3D operator+(const Field2D& lhs, const Field3D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, lhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, lhs.getRegion("RGN_ALL")) { const auto base_ind = localmesh->ind2Dto3D(index); for (int jz = 0; jz < localmesh->LocalNz; ++jz) { result[base_ind + jz] = lhs[index] + rhs[base_ind + jz]; } } - checkData(result); return result; } @@ -715,13 +1013,12 @@ Field3D operator-(const Field2D& lhs, const Field3D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, lhs.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, lhs.getRegion("RGN_ALL")) { const auto base_ind = localmesh->ind2Dto3D(index); for (int jz = 0; jz < localmesh->LocalNz; ++jz) { result[base_ind + jz] = lhs[index] - rhs[base_ind + jz]; } } - checkData(result); return result; } @@ -734,10 +1031,9 @@ Field2D operator*(const Field2D& lhs, const Field2D& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] * rhs[index]; } - checkData(result); return result; } @@ -752,7 +1048,7 @@ Field2D& Field2D::operator*=(const Field2D& rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } checkData(*this); @@ -770,10 +1066,9 @@ Field2D operator/(const Field2D& lhs, const Field2D& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] / rhs[index]; } - checkData(result); return result; } @@ -788,7 +1083,7 @@ Field2D& Field2D::operator/=(const Field2D& rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } checkData(*this); @@ -806,10 +1101,9 @@ Field2D operator+(const Field2D& lhs, const Field2D& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] + rhs[index]; } - checkData(result); return result; } @@ -824,7 +1118,7 @@ Field2D& Field2D::operator+=(const Field2D& rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } checkData(*this); @@ -842,10 +1136,9 @@ Field2D operator-(const Field2D& lhs, const Field2D& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] - rhs[index]; } - checkData(result); return result; } @@ -860,7 +1153,7 @@ Field2D& Field2D::operator-=(const Field2D& rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } checkData(*this); @@ -880,12 +1173,11 @@ FieldPerp operator*(const Field2D& lhs, const FieldPerp& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = rhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[base_ind] * rhs[index]; } - checkData(result); return result; } @@ -900,12 +1192,11 @@ FieldPerp operator/(const Field2D& lhs, const FieldPerp& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = rhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[base_ind] / rhs[index]; } - checkData(result); return result; } @@ -920,12 +1211,11 @@ FieldPerp operator+(const Field2D& lhs, const FieldPerp& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = rhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[base_ind] + rhs[index]; } - checkData(result); return result; } @@ -940,12 +1230,11 @@ FieldPerp operator-(const Field2D& lhs, const FieldPerp& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = rhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[base_ind] - rhs[index]; } - checkData(result); return result; } @@ -957,10 +1246,9 @@ Field2D operator*(const Field2D& lhs, const BoutReal rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] * rhs; } - checkData(result); return result; } @@ -974,7 +1262,7 @@ Field2D& Field2D::operator*=(const BoutReal rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } checkData(*this); @@ -992,10 +1280,9 @@ Field2D operator/(const Field2D& lhs, const BoutReal rhs) { checkData(rhs); const auto tmp = 1.0 / rhs; - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] * tmp; } - checkData(result); return result; } @@ -1010,7 +1297,7 @@ Field2D& Field2D::operator/=(const BoutReal rhs) { checkData(rhs); const auto tmp = 1.0 / rhs; - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= tmp; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= tmp; } checkData(*this); @@ -1027,10 +1314,9 @@ Field2D operator+(const Field2D& lhs, const BoutReal rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] + rhs; } - checkData(result); return result; } @@ -1044,7 +1330,7 @@ Field2D& Field2D::operator+=(const BoutReal rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } checkData(*this); @@ -1061,10 +1347,9 @@ Field2D operator-(const Field2D& lhs, const BoutReal rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] - rhs; } - checkData(result); return result; } @@ -1078,7 +1363,7 @@ Field2D& Field2D::operator-=(const BoutReal rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } checkData(*this); @@ -1098,12 +1383,11 @@ FieldPerp operator*(const FieldPerp& lhs, const Field3D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = lhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[index] * rhs[base_ind]; } - checkData(result); return result; } @@ -1120,7 +1404,7 @@ FieldPerp& FieldPerp::operator*=(const Field3D& rhs) { Mesh* localmesh = this->getMesh(); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { int yind = this->getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); (*this)[index] *= rhs[base_ind]; @@ -1144,12 +1428,11 @@ FieldPerp operator/(const FieldPerp& lhs, const Field3D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = lhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[index] / rhs[base_ind]; } - checkData(result); return result; } @@ -1166,7 +1449,7 @@ FieldPerp& FieldPerp::operator/=(const Field3D& rhs) { Mesh* localmesh = this->getMesh(); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { int yind = this->getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); (*this)[index] /= rhs[base_ind]; @@ -1190,12 +1473,11 @@ FieldPerp operator+(const FieldPerp& lhs, const Field3D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = lhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[index] + rhs[base_ind]; } - checkData(result); return result; } @@ -1212,7 +1494,7 @@ FieldPerp& FieldPerp::operator+=(const Field3D& rhs) { Mesh* localmesh = this->getMesh(); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { int yind = this->getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); (*this)[index] += rhs[base_ind]; @@ -1236,12 +1518,11 @@ FieldPerp operator-(const FieldPerp& lhs, const Field3D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = lhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[index] - rhs[base_ind]; } - checkData(result); return result; } @@ -1258,7 +1539,7 @@ FieldPerp& FieldPerp::operator-=(const Field3D& rhs) { Mesh* localmesh = this->getMesh(); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { int yind = this->getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); (*this)[index] -= rhs[base_ind]; @@ -1282,12 +1563,11 @@ FieldPerp operator*(const FieldPerp& lhs, const Field2D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = lhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[index] * rhs[base_ind]; } - checkData(result); return result; } @@ -1304,7 +1584,7 @@ FieldPerp& FieldPerp::operator*=(const Field2D& rhs) { Mesh* localmesh = this->getMesh(); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { int yind = this->getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); (*this)[index] *= rhs[base_ind]; @@ -1328,12 +1608,11 @@ FieldPerp operator/(const FieldPerp& lhs, const Field2D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = lhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[index] / rhs[base_ind]; } - checkData(result); return result; } @@ -1350,7 +1629,7 @@ FieldPerp& FieldPerp::operator/=(const Field2D& rhs) { Mesh* localmesh = this->getMesh(); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { int yind = this->getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); (*this)[index] /= rhs[base_ind]; @@ -1374,12 +1653,11 @@ FieldPerp operator+(const FieldPerp& lhs, const Field2D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = lhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[index] + rhs[base_ind]; } - checkData(result); return result; } @@ -1396,7 +1674,7 @@ FieldPerp& FieldPerp::operator+=(const Field2D& rhs) { Mesh* localmesh = this->getMesh(); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { int yind = this->getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); (*this)[index] += rhs[base_ind]; @@ -1420,12 +1698,11 @@ FieldPerp operator-(const FieldPerp& lhs, const Field2D& rhs) { Mesh* localmesh = lhs.getMesh(); - BOUT_FOR(index, result.getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getRegion("RGN_ALL")) { int yind = lhs.getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); result[index] = lhs[index] - rhs[base_ind]; } - checkData(result); return result; } @@ -1442,7 +1719,7 @@ FieldPerp& FieldPerp::operator-=(const Field2D& rhs) { Mesh* localmesh = this->getMesh(); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { int yind = this->getIndex(); const auto base_ind = localmesh->indPerpto3D(index, yind); (*this)[index] -= rhs[base_ind]; @@ -1464,10 +1741,9 @@ FieldPerp operator*(const FieldPerp& lhs, const FieldPerp& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] * rhs[index]; } - checkData(result); return result; } @@ -1482,7 +1758,7 @@ FieldPerp& FieldPerp::operator*=(const FieldPerp& rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } checkData(*this); @@ -1500,10 +1776,9 @@ FieldPerp operator/(const FieldPerp& lhs, const FieldPerp& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] / rhs[index]; } - checkData(result); return result; } @@ -1518,7 +1793,7 @@ FieldPerp& FieldPerp::operator/=(const FieldPerp& rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } checkData(*this); @@ -1536,10 +1811,9 @@ FieldPerp operator+(const FieldPerp& lhs, const FieldPerp& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] + rhs[index]; } - checkData(result); return result; } @@ -1554,7 +1828,7 @@ FieldPerp& FieldPerp::operator+=(const FieldPerp& rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } checkData(*this); @@ -1572,10 +1846,9 @@ FieldPerp operator-(const FieldPerp& lhs, const FieldPerp& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] - rhs[index]; } - checkData(result); return result; } @@ -1590,7 +1863,7 @@ FieldPerp& FieldPerp::operator-=(const FieldPerp& rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } checkData(*this); @@ -1607,10 +1880,9 @@ FieldPerp operator*(const FieldPerp& lhs, const BoutReal rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] * rhs; } - checkData(result); return result; } @@ -1624,7 +1896,7 @@ FieldPerp& FieldPerp::operator*=(const BoutReal rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } checkData(*this); @@ -1642,10 +1914,9 @@ FieldPerp operator/(const FieldPerp& lhs, const BoutReal rhs) { checkData(rhs); const auto tmp = 1.0 / rhs; - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] * tmp; } - checkData(result); return result; } @@ -1659,7 +1930,7 @@ FieldPerp& FieldPerp::operator/=(const BoutReal rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs; } checkData(*this); @@ -1676,10 +1947,9 @@ FieldPerp operator+(const FieldPerp& lhs, const BoutReal rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] + rhs; } - checkData(result); return result; } @@ -1693,7 +1963,7 @@ FieldPerp& FieldPerp::operator+=(const BoutReal rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } checkData(*this); @@ -1710,10 +1980,9 @@ FieldPerp operator-(const FieldPerp& lhs, const BoutReal rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs[index] - rhs; } - checkData(result); return result; } @@ -1727,7 +1996,7 @@ FieldPerp& FieldPerp::operator-=(const BoutReal rhs) { checkData(*this); checkData(rhs); - BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } checkData(*this); @@ -1746,10 +2015,9 @@ Field3D operator*(const BoutReal lhs, const Field3D& rhs) { result.setRegion(rhs.getRegionID()); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs * rhs[index]; } - checkData(result); return result; } @@ -1763,10 +2031,9 @@ Field3D operator/(const BoutReal lhs, const Field3D& rhs) { result.setRegion(rhs.getRegionID()); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs / rhs[index]; } - checkData(result); return result; } @@ -1780,10 +2047,9 @@ Field3D operator+(const BoutReal lhs, const Field3D& rhs) { result.setRegion(rhs.getRegionID()); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs + rhs[index]; } - checkData(result); return result; } @@ -1797,10 +2063,9 @@ Field3D operator-(const BoutReal lhs, const Field3D& rhs) { result.setRegion(rhs.getRegionID()); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs - rhs[index]; } - checkData(result); return result; } @@ -1812,10 +2077,9 @@ Field2D operator*(const BoutReal lhs, const Field2D& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs * rhs[index]; } - checkData(result); return result; } @@ -1827,10 +2091,9 @@ Field2D operator/(const BoutReal lhs, const Field2D& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs / rhs[index]; } - checkData(result); return result; } @@ -1842,10 +2105,9 @@ Field2D operator+(const BoutReal lhs, const Field2D& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs + rhs[index]; } - checkData(result); return result; } @@ -1857,10 +2119,9 @@ Field2D operator-(const BoutReal lhs, const Field2D& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs - rhs[index]; } - checkData(result); return result; } @@ -1872,10 +2133,9 @@ FieldPerp operator*(const BoutReal lhs, const FieldPerp& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs * rhs[index]; } - checkData(result); return result; } @@ -1887,10 +2147,9 @@ FieldPerp operator/(const BoutReal lhs, const FieldPerp& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs / rhs[index]; } - checkData(result); return result; } @@ -1902,10 +2161,9 @@ FieldPerp operator+(const BoutReal lhs, const FieldPerp& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs + rhs[index]; } - checkData(result); return result; } @@ -1917,10 +2175,1254 @@ FieldPerp operator-(const BoutReal lhs, const FieldPerp& rhs) { checkData(lhs); checkData(rhs); - BOUT_FOR(index, result.getValidRegionWithDefault("RGN_ALL")) { + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { result[index] = lhs - rhs[index]; } + checkData(result); + return result; +} + +// Provide the C++ wrapper for multiplication of Field3D and Field3DParallel +Field3DParallel operator*(const Field3D& lhs, const Field3DParallel& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(rhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) * rhs.yup(i); + result.ydown(i) = lhs.ydown(i) * rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] * rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ wrapper for division of Field3D and Field3DParallel +Field3DParallel operator/(const Field3D& lhs, const Field3DParallel& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(rhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) / rhs.yup(i); + result.ydown(i) = lhs.ydown(i) / rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] / rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ wrapper for addition of Field3D and Field3DParallel +Field3DParallel operator+(const Field3D& lhs, const Field3DParallel& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(rhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) + rhs.yup(i); + result.ydown(i) = lhs.ydown(i) + rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] + rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ wrapper for subtraction of Field3D and Field3DParallel +Field3DParallel operator-(const Field3D& lhs, const Field3DParallel& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(rhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) - rhs.yup(i); + result.ydown(i) = lhs.ydown(i) - rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] - rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ wrapper for multiplication of Field3DParallel and Field3D +Field3DParallel operator*(const Field3DParallel& lhs, const Field3D& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) * rhs.yup(i); + result.ydown(i) = lhs.ydown(i) * rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] * rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by multiplication with Field3D +Field3DParallel& Field3DParallel::operator*=(const Field3D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_multiplication_inplace(rhs.yup(i)); + ydown(i).update_multiplication_inplace(rhs.ydown(i)); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) *= rhs.yup(i); + ydown(i) *= rhs.ydown(i); + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } + track(rhs, "operator*="); + + checkData(*this); + + } else { + track(rhs, "operator*="); + (*this) = (*this) * rhs; + } + return *this; +} + +// Provide the C++ wrapper for division of Field3DParallel and Field3D +Field3DParallel operator/(const Field3DParallel& lhs, const Field3D& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) / rhs.yup(i); + result.ydown(i) = lhs.ydown(i) / rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] / rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by division with Field3D +Field3DParallel& Field3DParallel::operator/=(const Field3D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_division_inplace(rhs.yup(i)); + ydown(i).update_division_inplace(rhs.ydown(i)); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) /= rhs.yup(i); + ydown(i) /= rhs.ydown(i); + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } + track(rhs, "operator/="); + + checkData(*this); + + } else { + track(rhs, "operator/="); + (*this) = (*this) / rhs; + } + return *this; +} + +// Provide the C++ wrapper for addition of Field3DParallel and Field3D +Field3DParallel operator+(const Field3DParallel& lhs, const Field3D& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) + rhs.yup(i); + result.ydown(i) = lhs.ydown(i) + rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] + rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by addition with Field3D +Field3DParallel& Field3DParallel::operator+=(const Field3D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_addition_inplace(rhs.yup(i)); + ydown(i).update_addition_inplace(rhs.ydown(i)); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) += rhs.yup(i); + ydown(i) += rhs.ydown(i); + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } + track(rhs, "operator+="); + + checkData(*this); + + } else { + track(rhs, "operator+="); + (*this) = (*this) + rhs; + } + return *this; +} + +// Provide the C++ wrapper for subtraction of Field3DParallel and Field3D +Field3DParallel operator-(const Field3DParallel& lhs, const Field3D& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) - rhs.yup(i); + result.ydown(i) = lhs.ydown(i) - rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] - rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by subtraction with Field3D +Field3DParallel& Field3DParallel::operator-=(const Field3D& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_subtraction_inplace(rhs.yup(i)); + ydown(i).update_subtraction_inplace(rhs.ydown(i)); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) -= rhs.yup(i); + ydown(i) -= rhs.ydown(i); + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } + track(rhs, "operator-="); + + checkData(*this); + + } else { + track(rhs, "operator-="); + (*this) = (*this) - rhs; + } + return *this; +} + +// Provide the C++ wrapper for multiplication of Field3DParallel and Field3DParallel +Field3DParallel operator*(const Field3DParallel& lhs, const Field3DParallel& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) * rhs.yup(i); + result.ydown(i) = lhs.ydown(i) * rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] * rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by multiplication with Field3DParallel +Field3DParallel& Field3DParallel::operator*=(const Field3DParallel& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_multiplication_inplace(rhs); + ydown(i).update_multiplication_inplace(rhs); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) *= rhs; + ydown(i) *= rhs; + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } + track(rhs, "operator*="); + + checkData(*this); + + } else { + track(rhs, "operator*="); + (*this) = (*this) * rhs; + } + return *this; +} + +// Provide the C++ wrapper for division of Field3DParallel and Field3DParallel +Field3DParallel operator/(const Field3DParallel& lhs, const Field3DParallel& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) / rhs.yup(i); + result.ydown(i) = lhs.ydown(i) / rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] / rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by division with Field3DParallel +Field3DParallel& Field3DParallel::operator/=(const Field3DParallel& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_division_inplace(rhs); + ydown(i).update_division_inplace(rhs); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) /= rhs; + ydown(i) /= rhs; + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } + track(rhs, "operator/="); + + checkData(*this); + + } else { + track(rhs, "operator/="); + (*this) = (*this) / rhs; + } + return *this; +} + +// Provide the C++ wrapper for addition of Field3DParallel and Field3DParallel +Field3DParallel operator+(const Field3DParallel& lhs, const Field3DParallel& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) + rhs.yup(i); + result.ydown(i) = lhs.ydown(i) + rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] + rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by addition with Field3DParallel +Field3DParallel& Field3DParallel::operator+=(const Field3DParallel& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_addition_inplace(rhs); + ydown(i).update_addition_inplace(rhs); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) += rhs; + ydown(i) += rhs; + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } + track(rhs, "operator+="); + + checkData(*this); + + } else { + track(rhs, "operator+="); + (*this) = (*this) + rhs; + } + return *this; +} + +// Provide the C++ wrapper for subtraction of Field3DParallel and Field3DParallel +Field3DParallel operator-(const Field3DParallel& lhs, const Field3DParallel& rhs) { + ASSERT1_FIELDS_COMPATIBLE(lhs, rhs); + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getMesh()->getCommonRegion(lhs.getRegionID(), rhs.getRegionID())); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) - rhs.yup(i); + result.ydown(i) = lhs.ydown(i) - rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] - rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by subtraction with Field3DParallel +Field3DParallel& Field3DParallel::operator-=(const Field3DParallel& rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + ASSERT1_FIELDS_COMPATIBLE(*this, rhs); + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_subtraction_inplace(rhs); + ydown(i).update_subtraction_inplace(rhs); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) -= rhs; + ydown(i) -= rhs; + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + regionID = fieldmesh->getCommonRegion(regionID, rhs.getRegionID()); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } + track(rhs, "operator-="); + + checkData(*this); + + } else { + track(rhs, "operator-="); + (*this) = (*this) - rhs; + } + return *this; +} + +// Provide the C++ wrapper for multiplication of Field3DParallel and BoutReal +Field3DParallel operator*(const Field3DParallel& lhs, const BoutReal rhs) { + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getRegionID()); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) * rhs; + result.ydown(i) = lhs.ydown(i) * rhs; + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] * rhs; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by multiplication with BoutReal +Field3DParallel& Field3DParallel::operator*=(const BoutReal rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_multiplication_inplace(rhs); + ydown(i).update_multiplication_inplace(rhs); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) *= rhs; + ydown(i) *= rhs; + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } + track(rhs, "operator*="); + + checkData(*this); + + } else { + track(rhs, "operator*="); + (*this) = (*this) * rhs; + } + return *this; +} + +// Provide the C++ wrapper for division of Field3DParallel and BoutReal +Field3DParallel operator/(const Field3DParallel& lhs, const BoutReal rhs) { + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getRegionID()); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) / rhs; + result.ydown(i) = lhs.ydown(i) / rhs; + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + const auto tmp = 1.0 / rhs; + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] * tmp; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by division with BoutReal +Field3DParallel& Field3DParallel::operator/=(const BoutReal rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_division_inplace(rhs); + ydown(i).update_division_inplace(rhs); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) /= rhs; + ydown(i) /= rhs; + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs; } + track(rhs, "operator/="); + + checkData(*this); + + } else { + track(rhs, "operator/="); + (*this) = (*this) / rhs; + } + return *this; +} + +// Provide the C++ wrapper for addition of Field3DParallel and BoutReal +Field3DParallel operator+(const Field3DParallel& lhs, const BoutReal rhs) { + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getRegionID()); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) + rhs; + result.ydown(i) = lhs.ydown(i) + rhs; + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] + rhs; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by addition with BoutReal +Field3DParallel& Field3DParallel::operator+=(const BoutReal rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_addition_inplace(rhs); + ydown(i).update_addition_inplace(rhs); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) += rhs; + ydown(i) += rhs; + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } + track(rhs, "operator+="); + + checkData(*this); + + } else { + track(rhs, "operator+="); + (*this) = (*this) + rhs; + } + return *this; +} + +// Provide the C++ wrapper for subtraction of Field3DParallel and BoutReal +Field3DParallel operator-(const Field3DParallel& lhs, const BoutReal rhs) { + + Field3DParallel result{emptyFrom(lhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(lhs.getRegionID()); + if (result.isFci()) { + + ASSERT2(lhs.hasParallelSlices()); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + ASSERT2(lhs.ydown(i).isAllocated()); + ASSERT2(lhs.yup(i).isAllocated()); + } + + result.splitParallelSlices(); + for (size_t i{0}; i < lhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs.yup(i) - rhs; + result.ydown(i) = lhs.ydown(i) - rhs; + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs[index] - rhs; + } + checkData(result); + return result; +} + +// Provide the C++ operator to update Field3DParallel by subtraction with BoutReal +Field3DParallel& Field3DParallel::operator-=(const BoutReal rhs) { + // only if data is unique we update the field + // otherwise just call the non-inplace version + if (data.unique() or isRef) { + + if (this->isFci()) { + if (isRef) { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i).update_subtraction_inplace(rhs); + ydown(i).update_subtraction_inplace(rhs); + } + } else { + for (size_t i{0}; i < yup_fields.size(); ++i) { + yup(i) -= rhs; + ydown(i) -= rhs; + } + } + } else { + clearParallelSlices(); + } + checkData(*this); + checkData(rhs); + + BOUT_FOR_SERIAL(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } + track(rhs, "operator-="); + + checkData(*this); + + } else { + track(rhs, "operator-="); + (*this) = (*this) - rhs; + } + return *this; +} + +// Provide the C++ wrapper for multiplication of BoutReal and Field3DParallel +Field3DParallel operator*(const BoutReal lhs, const Field3DParallel& rhs) { + + Field3DParallel result{emptyFrom(rhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(rhs.getRegionID()); + if (result.isFci()) { + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs * rhs.yup(i); + result.ydown(i) = lhs * rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs * rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ wrapper for division of BoutReal and Field3DParallel +Field3DParallel operator/(const BoutReal lhs, const Field3DParallel& rhs) { + + Field3DParallel result{emptyFrom(rhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(rhs.getRegionID()); + if (result.isFci()) { + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs / rhs.yup(i); + result.ydown(i) = lhs / rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs / rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ wrapper for addition of BoutReal and Field3DParallel +Field3DParallel operator+(const BoutReal lhs, const Field3DParallel& rhs) { + + Field3DParallel result{emptyFrom(rhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(rhs.getRegionID()); + if (result.isFci()) { + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs + rhs.yup(i); + result.ydown(i) = lhs + rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs + rhs[index]; + } + checkData(result); + return result; +} + +// Provide the C++ wrapper for subtraction of BoutReal and Field3DParallel +Field3DParallel operator-(const BoutReal lhs, const Field3DParallel& rhs) { + + Field3DParallel result{emptyFrom(rhs)}; + checkData(lhs); + checkData(rhs); + + result.setRegion(rhs.getRegionID()); + if (result.isFci()) { + + ASSERT2(rhs.hasParallelSlices()); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + ASSERT2(rhs.ydown(i).isAllocated()); + ASSERT2(rhs.yup(i).isAllocated()); + } + result.splitParallelSlices(); + for (size_t i{0}; i < rhs.numberParallelSlices(); ++i) { + result.yup(i) = lhs - rhs.yup(i); + result.ydown(i) = lhs - rhs.ydown(i); + } + + ASSERT2(result.hasParallelSlices()); + for (size_t i{0}; i < result.numberParallelSlices(); ++i) { + ASSERT2(result.ydown(i).isAllocated()); + ASSERT2(result.yup(i).isAllocated()); + } + } + + BOUT_FOR_SERIAL(index, result.getValidRegionWithDefault("RGN_ALL")) { + result[index] = lhs - rhs[index]; + } checkData(result); return result; } diff --git a/src/field/initialprofiles.cxx b/src/field/initialprofiles.cxx index ded5aa9882..6a437dbe02 100644 --- a/src/field/initialprofiles.cxx +++ b/src/field/initialprofiles.cxx @@ -1,23 +1,10 @@ /************************************************************************** * Sets initial profiles * - * ChangeLog - * ========= - * - * 2011-02-12 Ben Dudson - * * Changed to use new options system. For now the structure of the - * options is the same, but this could be modified more easily in future - * - * 2010-05-12 Ben Dudson - * - * * Changed random numbers to use a hash of the parameters - * so that the phase doesn't vary with number of processors or grid size - * User can vary phase to give a different random sequence - * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -42,21 +29,17 @@ #include #include #include -#include void initial_profile(const std::string& name, Field3D& var) { - AUTO_TRACE(); Mesh* localmesh = var.getMesh(); Options* varOpts = Options::getRoot()->getSection(name); - FieldFactory f(localmesh); - std::string function; VAROPTION(varOpts, function, "0.0"); - var = f.create3D(function, varOpts, nullptr, var.getLocation()); + var = FieldFactory::get()->create3D(function, varOpts, localmesh, var.getLocation()); // Optionally scale the variable BoutReal scale; @@ -65,18 +48,15 @@ void initial_profile(const std::string& name, Field3D& var) { } void initial_profile(const std::string& name, Field2D& var) { - AUTO_TRACE(); Mesh* localmesh = var.getMesh(); Options* varOpts = Options::getRoot()->getSection(name); - FieldFactory f(localmesh); - std::string function; VAROPTION(varOpts, function, "0.0"); - var = f.create2D(function, varOpts, nullptr, var.getLocation()); + var = FieldFactory::get()->create2D(function, varOpts, localmesh, var.getLocation()); // Optionally scale the variable BoutReal scale; @@ -85,7 +65,6 @@ void initial_profile(const std::string& name, Field2D& var) { } void initial_profile(const std::string& name, Vector2D& var) { - AUTO_TRACE(); if (var.covariant) { initial_profile(name + "_x", var.x); @@ -99,7 +78,6 @@ void initial_profile(const std::string& name, Vector2D& var) { } void initial_profile(const std::string& name, Vector3D& var) { - AUTO_TRACE(); if (var.covariant) { initial_profile(name + "_x", var.x); diff --git a/src/field/vecops.cxx b/src/field/vecops.cxx index 5f34e2af02..672e8f6c09 100644 --- a/src/field/vecops.cxx +++ b/src/field/vecops.cxx @@ -1,12 +1,11 @@ /************************************************************************** * Operators on vector objects - * B.Dudson, October 2007 * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * - * Contact: Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -29,7 +28,6 @@ #include #include -#include #include #include #include @@ -39,7 +37,7 @@ **************************************************************************/ Vector2D Grad(const Field2D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Grad( Field2D )"); + SCOREP0(); CELL_LOC outloc_x, outloc_y, outloc_z; if (outloc == CELL_VSHIFT) { @@ -68,7 +66,7 @@ Vector2D Grad(const Field2D& f, CELL_LOC outloc, const std::string& method) { } Vector3D Grad(const Field3D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Grad( Field3D )"); + SCOREP0(); CELL_LOC outloc_x, outloc_y, outloc_z; if (outloc == CELL_VSHIFT) { @@ -97,7 +95,7 @@ Vector3D Grad(const Field3D& f, CELL_LOC outloc, const std::string& method) { } Vector3D Grad_perp(const Field3D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Grad_perp( Field3D )"); + SCOREP0(); ASSERT1(outloc == CELL_DEFAULT || outloc == f.getLocation()); @@ -119,7 +117,7 @@ Vector3D Grad_perp(const Field3D& f, CELL_LOC outloc, const std::string& method) } Vector2D Grad_perp(const Field2D& f, CELL_LOC outloc, const std::string& method) { - AUTO_TRACE(); + SCOREP0(); ASSERT1(outloc == CELL_DEFAULT || outloc == f.getLocation()); @@ -145,7 +143,7 @@ Vector2D Grad_perp(const Field2D& f, CELL_LOC outloc, const std::string& method) Coordinates::FieldMetric Div(const Vector2D& v, CELL_LOC outloc, const std::string& method) { - TRACE("Div( Vector2D )"); + SCOREP0(); if (outloc == CELL_DEFAULT) { outloc = v.getLocation(); @@ -162,15 +160,15 @@ Coordinates::FieldMetric Div(const Vector2D& v, CELL_LOC outloc, vcn.toContravariant(); Coordinates::FieldMetric result = DDX(metric->J * vcn.x, outloc, method); - result += DDY(metric->J * vcn.y, outloc, method); - result += DDZ(metric->J * vcn.z, outloc, method); + result += DDY(Coordinates::FieldMetric{metric->J * vcn.y}, outloc, method); + result += DDZ(Coordinates::FieldMetric{metric->J * vcn.z}, outloc, method); result /= metric->J; return result; } Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) { - TRACE("Div( Vector3D )"); + SCOREP0(); if (outloc == CELL_DEFAULT) { outloc = v.getLocation(); @@ -187,7 +185,7 @@ Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) { Vector3D vcn = v; vcn.toContravariant(); - auto vcnJy = vcn.y.getCoordinates()->J * vcn.y; + Field3D vcnJy = vcn.y.getCoordinates()->J * vcn.y; if (v.y.hasParallelSlices()) { // If v.y has parallel slices then we are using ShiftedMetric (with // mesh:calcParallelSlices_on_communicate=true) or FCI, so we should calculate @@ -196,8 +194,8 @@ Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) { } auto result = DDY(vcnJy, outloc, method); - result += DDX(vcn.x.getCoordinates()->J * vcn.x, outloc, method); - result += DDZ(vcn.z.getCoordinates()->J * vcn.z, outloc, method); + result += DDX(Field3D{vcn.x.getCoordinates()->J * vcn.x}, outloc, method); + result += DDZ(Field3D{vcn.z.getCoordinates()->J * vcn.z}, outloc, method); result /= metric->J; return result; @@ -209,7 +207,7 @@ Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) { Coordinates::FieldMetric Div(const Vector2D& v, const Field2D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Div( Vector2D, Field2D )"); + SCOREP0(); if (outloc == CELL_DEFAULT) { outloc = v.getLocation(); @@ -225,10 +223,12 @@ Coordinates::FieldMetric Div(const Vector2D& v, const Field2D& f, CELL_LOC outlo Vector2D vcn = v; vcn.toContravariant(); - Coordinates::FieldMetric result = - FDDX(vcn.x.getCoordinates()->J * vcn.x, f, outloc, method); - result += FDDY(vcn.y.getCoordinates()->J * vcn.y, f, outloc, method); - result += FDDZ(vcn.z.getCoordinates()->J * vcn.z, f, outloc, method); + Coordinates::FieldMetric result = FDDX( + Coordinates::FieldMetric{vcn.x.getCoordinates()->J * vcn.x}, f, outloc, method); + result += FDDY(Coordinates::FieldMetric{vcn.y.getCoordinates()->J * vcn.y}, f, outloc, + method); + result += FDDZ(Coordinates::FieldMetric{vcn.z.getCoordinates()->J * vcn.z}, f, outloc, + method); result /= metric->J; return result; @@ -236,7 +236,6 @@ Coordinates::FieldMetric Div(const Vector2D& v, const Field2D& f, CELL_LOC outlo Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Div( Vector3D, Field3D )"); if (outloc == CELL_DEFAULT) { outloc = v.getLocation(); @@ -251,9 +250,9 @@ Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, Vector3D vcn = v; vcn.toContravariant(); - Field3D result = FDDX(vcn.x.getCoordinates()->J * vcn.x, f, outloc, method); - result += FDDY(vcn.y.getCoordinates()->J * vcn.y, f, outloc, method); - result += FDDZ(vcn.z.getCoordinates()->J * vcn.z, f, outloc, method); + Field3D result = FDDX(Field3D{vcn.x.getCoordinates()->J * vcn.x}, f, outloc, method); + result += FDDY(Field3D{vcn.y.getCoordinates()->J * vcn.y}, f, outloc, method); + result += FDDZ(Field3D{vcn.z.getCoordinates()->J * vcn.z}, f, outloc, method); result /= metric->J; return result; @@ -265,8 +264,6 @@ Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, Vector2D Curl(const Vector2D& v) { - TRACE("Curl( Vector2D )"); - ASSERT1(v.getLocation() != CELL_VSHIFT); Mesh* localmesh = v.getMesh(); auto metric = v.x.getCoordinates(); @@ -292,7 +289,7 @@ Vector2D Curl(const Vector2D& v) { } Vector3D Curl(const Vector3D& v) { - TRACE("Curl( Vector3D )"); + SCOREP0(); ASSERT1(v.getLocation() != CELL_VSHIFT); @@ -323,7 +320,7 @@ Vector3D Curl(const Vector3D& v) { * Upwinding operators **************************************************************************/ Coordinates::FieldMetric V_dot_Grad(const Vector2D& v, const Field2D& f) { - TRACE("V_dot_Grad( Vector2D , Field2D )"); + SCOREP0(); // Get contravariant components of v @@ -334,7 +331,7 @@ Coordinates::FieldMetric V_dot_Grad(const Vector2D& v, const Field2D& f) { } Field3D V_dot_Grad(const Vector2D& v, const Field3D& f) { - TRACE("V_dot_Grad( Vector2D , Field3D )"); + SCOREP0(); // Get contravariant components of v @@ -345,7 +342,7 @@ Field3D V_dot_Grad(const Vector2D& v, const Field3D& f) { } Field3D V_dot_Grad(const Vector3D& v, const Field2D& f) { - TRACE("V_dot_Grad( Vector3D , Field2D )"); + SCOREP0(); // Get contravariant components of v @@ -356,7 +353,7 @@ Field3D V_dot_Grad(const Vector3D& v, const Field2D& f) { } Field3D V_dot_Grad(const Vector3D& v, const Field3D& f) { - TRACE("V_dot_Grad( Vector3D , Field3D )"); + SCOREP0(); // Get contravariant components of v @@ -370,7 +367,7 @@ Field3D V_dot_Grad(const Vector3D& v, const Field3D& f) { // operation (addition) between the two input types. template R V_dot_Grad(const T& v, const F& a) { - AUTO_TRACE(); + SCOREP0(); ASSERT1(v.getLocation() == a.getLocation()); ASSERT1(v.getLocation() != CELL_VSHIFT); diff --git a/src/field/vector2d.cxx b/src/field/vector2d.cxx index 74d3a88a22..ab2d993549 100644 --- a/src/field/vector2d.cxx +++ b/src/field/vector2d.cxx @@ -429,7 +429,7 @@ CELL_LOC Vector2D::getLocation() const { Vector2D& Vector2D::setLocation(CELL_LOC loc) { SCOREP0(); - TRACE("Vector2D::setLocation"); + if (loc == CELL_DEFAULT) { loc = CELL_CENTRE; } diff --git a/src/field/vector3d.cxx b/src/field/vector3d.cxx index 56124595b3..7fb4a88918 100644 --- a/src/field/vector3d.cxx +++ b/src/field/vector3d.cxx @@ -551,7 +551,7 @@ CELL_LOC Vector3D::getLocation() const { Vector3D& Vector3D::setLocation(CELL_LOC loc) { SCOREP0(); - TRACE("Vector3D::setLocation"); + if (loc == CELL_DEFAULT) { loc = CELL_CENTRE; } diff --git a/src/invert/fft_fftw.cxx b/src/invert/fft_fftw.cxx index 05977e5f3f..2c633da6d5 100644 --- a/src/invert/fft_fftw.cxx +++ b/src/invert/fft_fftw.cxx @@ -27,8 +27,10 @@ #include "bout/build_defines.hxx" +#include #include #include +#include #include #include @@ -36,7 +38,6 @@ #include #include -#include #include #if BOUT_USE_OPENMP @@ -46,6 +47,12 @@ #include #endif // BOUT_HAS_FFTW +#if BOUT_CHECK_LEVEL > 0 +#include + +#include +#endif + namespace bout { namespace fft { @@ -527,5 +534,14 @@ Array irfft(const Array& in, int length) { return out; } +#if BOUT_CHECK_LEVEL > 0 +void assertZSerial(const Mesh& mesh, std::string_view name) { + if (mesh.getNZPE() != 1) { + throw BoutException("{} uses FFTs which are currently incompatible with multiple " + "processors in Z (using {})", + name, mesh.getNZPE()); + } +} +#endif } // namespace fft } // namespace bout diff --git a/src/invert/lapack_routines.cxx b/src/invert/lapack_routines.cxx index 638385dfe8..1ccb021abb 100644 --- a/src/invert/lapack_routines.cxx +++ b/src/invert/lapack_routines.cxx @@ -2,7 +2,7 @@ * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -97,7 +97,7 @@ int tridag(const dcomplex* a, const dcomplex* b, const dcomplex* c, const dcompl } /* Real tridiagonal solver - * + * * Returns true on success */ bool tridag(const BoutReal* a, const BoutReal* b, const BoutReal* c, const BoutReal* r, @@ -148,7 +148,7 @@ bool tridag(const BoutReal* a, const BoutReal* b, const BoutReal* c, const BoutR } /* Real cyclic tridiagonal solver - * + * * Uses Sherman-Morrison formula */ void cyclic_tridag(BoutReal* a, BoutReal* b, BoutReal* c, BoutReal* r, BoutReal* x, @@ -198,7 +198,7 @@ void cyclic_tridag(BoutReal* a, BoutReal* b, BoutReal* c, BoutReal* r, BoutReal* } /*! Complex band solver using ZGBSV - * + * * n Size of the matrix (number of equations) * kl Number of subdiagonals * ku Number of superdiagonals diff --git a/src/invert/laplace/common_transform.cxx b/src/invert/laplace/common_transform.cxx new file mode 100644 index 0000000000..847add4fea --- /dev/null +++ b/src/invert/laplace/common_transform.cxx @@ -0,0 +1,381 @@ +#include "common_transform.hxx" + +#include "bout/array.hxx" +#include "bout/bout_types.hxx" +#include "bout/constants.hxx" +#include "bout/dcomplex.hxx" +#include "bout/fft.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" +#include "bout/fieldperp.hxx" +#include "bout/invert_laplace.hxx" +#include "bout/mesh.hxx" +#include "bout/openmpwrap.hxx" +#include "bout/utils.hxx" + +#include + +FFTTransform::FFTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, int ye, + int zs, int ze, int inbndry, int outbndry, + bool inner_boundary_set_on_first_x, + bool outer_boundary_set_on_last_x, bool zero_DC) + : zlength(getUniform(mesh.getCoordinatesConst()->zlength())), nmode(nmode), xs(xs), + xe(xe), ys(ys), ye(ye), zs(zs), ze(ze), nx(xe - xs + 1), ny(ye - ys + 1), + nz(ze - zs + 1), nxny(nx * ny), nsys(nmode * ny), inbndry(inbndry), + outbndry(outbndry), inner_boundary_set_on_first_x(inner_boundary_set_on_first_x), + outer_boundary_set_on_last_x(outer_boundary_set_on_last_x), zero_DC(zero_DC), + localmesh(&mesh) {} + +auto FFTTransform::forward(const Laplacian& laplacian, const Field3D& rhs, + const Field3D& x0, const Field2D& Acoef, const Field2D& C1coef, + const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices { + + Matrices result(nsys, nx); + + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZFFT routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + // Loop over X and Y indices, including boundaries but not guard cells + // (unless periodic in x) + BOUT_OMP_PERF(for) + for (int ind = 0; ind < nxny; ++ind) { + const int ix = xs + (ind / ny); + const int iy = ys + (ind % ny); + + // Take FFT in Z direction, apply shift, and put result in k1d + + if (((ix < inbndry) and inner_boundary_set_on_first_x) + || ((localmesh->LocalNx - ix - 1 < outbndry) + and outer_boundary_set_on_last_x)) { + // Use the values in x0 in the boundary + rfft(&(x0(ix, iy, zs)), nz, std::begin(k1d)); + } else { + rfft(&(rhs(ix, iy, zs)), nz, std::begin(k1d)); + } + + // Copy into array, transposing so kz is first index + for (int kz = 0; kz < nmode; kz++) { + result.bcmplx(((iy - ys) * nmode) + kz, ix - xs) = k1d[kz]; + } + } + + // Get elements of the tridiagonal matrix + // including boundary conditions + BOUT_OMP_PERF(for nowait) + for (int ind = 0; ind < nsys; ind++) { + const int iy = ys + (ind / nmode); + const int kz = ind % nmode; + + const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] + laplacian.tridagMatrix(&result.a(ind, 0), &result.b(ind, 0), &result.c(ind, 0), + &result.bcmplx(ind, 0), iy, kz, kwave, &Acoef, &C1coef, + &C2coef, &Dcoef, false); + } + } + return result; +} + +auto FFTTransform::backward(const Field3D& rhs, + const Matrix& xcmplx3D) const -> Field3D { + Field3D x{emptyFrom(rhs)}; + + // FFT back to real space + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZFFT routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + BOUT_OMP_PERF(for nowait) + for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y + const int ix = xs + (ind / ny); + const int iy = ys + (ind % ny); + + if (zero_DC) { + k1d[0] = 0.; + } + + for (int kz = static_cast(zero_DC); kz < nmode; kz++) { + k1d[kz] = xcmplx3D(((iy - ys) * nmode) + kz, ix - xs); + } + + for (int kz = nmode; kz < (nz / 2) + 1; kz++) { + k1d[kz] = 0.0; // Filtering out all higher harmonics + } + + irfft(std::begin(k1d), nz, &(x(ix, iy, zs))); + } + } + + return x; +} + +auto FFTTransform::forward(const Laplacian& laplacian, const FieldPerp& rhs, + const FieldPerp& x0, const Field2D& Acoef, + const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices { + + Matrices result(nmode, nx); + const int jy = rhs.getIndex(); + + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZFFT routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + // Loop over X indices, including boundaries but not guard + // cells (unless periodic in x) + BOUT_OMP_PERF(for) + for (int ix = xs; ix <= xe; ix++) { + // Take FFT in Z direction, apply shift, and put result in k1d + + if (((ix < inbndry) and inner_boundary_set_on_first_x) + || ((localmesh->LocalNx - ix - 1 < outbndry) + and outer_boundary_set_on_last_x)) { + // Use the values in x0 in the boundary + rfft(&(x0(ix, zs)), nz, std::begin(k1d)); + } else { + rfft(&(rhs(ix, zs)), nz, std::begin(k1d)); + } + + // Copy into array, transposing so kz is first index + for (int kz = 0; kz < nmode; kz++) { + result.bcmplx(kz, ix - xs) = k1d[kz]; + } + } + + // Get elements of the tridiagonal matrix + // including boundary conditions + BOUT_OMP_PERF(for nowait) + for (int kz = 0; kz < nmode; kz++) { + const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] + laplacian.tridagMatrix(&result.a(kz, 0), &result.b(kz, 0), &result.c(kz, 0), + &result.bcmplx(kz, 0), jy, kz, kwave, &Acoef, &C1coef, + &C2coef, &Dcoef, false); + } + } + return result; +} + +auto FFTTransform::backward(const FieldPerp& rhs, + const Matrix& xcmplx) const -> FieldPerp { + FieldPerp x{emptyFrom(rhs)}; + + // FFT back to real space + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZFFT routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + BOUT_OMP_PERF(for nowait) + for (int ix = xs; ix <= xe; ++ix) { + if (zero_DC) { + k1d[0] = 0.; + } + + for (int kz = static_cast(zero_DC); kz < nmode; kz++) { + k1d[kz] = xcmplx(kz, ix - xs); + } + + for (int kz = nmode; kz < (nz / 2) + 1; kz++) { + k1d[kz] = 0.0; // Filtering out all higher harmonics + } + + irfft(std::begin(k1d), nz, &(x(ix, zs))); + } + } + + checkData(x); + + return x; +} + +DSTTransform::DSTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, int ye, + int zs, int ze, int inbndry, int outbndry, + bool inner_boundary_set_on_first_x, + bool outer_boundary_set_on_last_x, bool zero_DC) + : zlength(getUniform(mesh.getCoordinatesConst()->zlength())), nmode(nmode), xs(xs), + xe(xe), ys(ys), ye(ye), zs(zs), ze(ze), nx(xe - xs + 1), ny(ye - ys + 1), + nz(ze - zs + 1), nxny(nx * ny), nsys(nmode * ny), inbndry(inbndry), + outbndry(outbndry), inner_boundary_set_on_first_x(inner_boundary_set_on_first_x), + outer_boundary_set_on_last_x(outer_boundary_set_on_last_x), zero_DC(zero_DC), + localmesh(&mesh) {} + +auto DSTTransform::forward(const Laplacian& laplacian, const Field3D& rhs, + const Field3D& x0, const Field2D& Acoef, const Field2D& C1coef, + const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices { + + Matrices result(nsys, nx); + + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZDST routine expects input of this length + auto k1d = Array(nz); + + // Loop over X and Y indices, including boundaries but not guard cells + // (unless periodic in x) + BOUT_OMP_PERF(for) + for (int ind = 0; ind < nxny; ++ind) { + const int ix = xs + (ind / ny); + const int iy = ys + (ind % ny); + + // Take DST in Z direction, apply shift, and put result in k1d + + if (((ix < inbndry) and inner_boundary_set_on_first_x) + || ((localmesh->LocalNx - ix - 1 < outbndry) + and outer_boundary_set_on_last_x)) { + // Use the values in x0 in the boundary + bout::fft::DST(&(x0(ix, iy, zs + 1)), nz - 2, std::begin(k1d)); + } else { + bout::fft::DST(&(rhs(ix, iy, zs + 1)), nz - 2, std::begin(k1d)); + } + + // Copy into array, transposing so kz is first index + for (int kz = 0; kz < nmode; kz++) { + result.bcmplx(((iy - ys) * nmode) + kz, ix - xs) = k1d[kz]; + } + } + + // Get elements of the tridiagonal matrix + // including boundary conditions + BOUT_OMP_PERF(for nowait) + for (int ind = 0; ind < nsys; ind++) { + const int iy = ys + (ind / nmode); + const int kz = ind % nmode; + + const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] + laplacian.tridagMatrix(&result.a(ind, 0), &result.b(ind, 0), &result.c(ind, 0), + &result.bcmplx(ind, 0), iy, kz, kwave, &Acoef, &C1coef, + &C2coef, &Dcoef, false); + } + } + return result; +} + +auto DSTTransform::backward(const Field3D& rhs, + const Matrix& xcmplx3D) const -> Field3D { + Field3D x{emptyFrom(rhs)}; + + // DST back to real space + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZDST routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + BOUT_OMP_PERF(for nowait) + for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y + const int ix = xs + (ind / ny); + const int iy = ys + (ind % ny); + + if (zero_DC) { + k1d[0] = 0.; + } + + for (int kz = static_cast(zero_DC); kz < nmode; kz++) { + k1d[kz] = xcmplx3D(((iy - ys) * nmode) + kz, ix - xs); + } + + for (int kz = nmode; kz < (nz / 2) + 1; kz++) { + k1d[kz] = 0.0; // Filtering out all higher harmonics + } + + bout::fft::DST_rev(std::begin(k1d), nz - 2, &(x(ix, iy, zs + 1))); + + x(ix, iy, zs) = -x(ix, iy, zs + 2); + x(ix, iy, ze) = -x(ix, iy, ze - 2); + } + } + + return x; +} + +auto DSTTransform::forward(const Laplacian& laplacian, const FieldPerp& rhs, + const FieldPerp& x0, const Field2D& Acoef, + const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices { + + Matrices result(nmode, nx); + const int jy = rhs.getIndex(); + + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZDST routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + // Loop over X indices, including boundaries but not guard + // cells (unless periodic in x) + BOUT_OMP_PERF(for) + for (int ix = xs; ix <= xe; ix++) { + // Take DST in Z direction, apply shift, and put result in k1d + + if (((ix < inbndry) and inner_boundary_set_on_first_x) + || ((localmesh->LocalNx - ix - 1 < outbndry) + and outer_boundary_set_on_last_x)) { + // Use the values in x0 in the boundary + rfft(&(x0(ix, zs)), nz, std::begin(k1d)); + } else { + rfft(&(rhs(ix, zs)), nz, std::begin(k1d)); + } + + // Copy into array, transposing so kz is first index + for (int kz = 0; kz < nmode; kz++) { + result.bcmplx(kz, ix - xs) = k1d[kz]; + } + } + + // Get elements of the tridiagonal matrix + // including boundary conditions + BOUT_OMP_PERF(for nowait) + for (int kz = 0; kz < nmode; kz++) { + const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] + laplacian.tridagMatrix(&result.a(kz, 0), &result.b(kz, 0), &result.c(kz, 0), + &result.bcmplx(kz, 0), jy, kz, kwave, &Acoef, &C1coef, + &C2coef, &Dcoef, false); + } + } + return result; +} + +auto DSTTransform::backward(const FieldPerp& rhs, + const Matrix& xcmplx) const -> FieldPerp { + FieldPerp x{emptyFrom(rhs)}; + + // DST back to real space + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZDST routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + BOUT_OMP_PERF(for nowait) + for (int ix = xs; ix < xe; ++ix) { + if (zero_DC) { + k1d[0] = 0.; + } + + for (int kz = static_cast(zero_DC); kz < nmode; kz++) { + k1d[kz] = xcmplx(kz, ix - xs); + } + + for (int kz = nmode; kz < (nz / 2) + 1; kz++) { + k1d[kz] = 0.0; // Filtering out all higher harmonics + } + + irfft(std::begin(k1d), nz, &(x(ix, zs))); + } + } + + checkData(x); + + return x; +} diff --git a/src/invert/laplace/common_transform.hxx b/src/invert/laplace/common_transform.hxx new file mode 100644 index 0000000000..a05ca2feb3 --- /dev/null +++ b/src/invert/laplace/common_transform.hxx @@ -0,0 +1,132 @@ +#pragma once + +#include "bout/bout_types.hxx" +#include "bout/dcomplex.hxx" +#include "bout/utils.hxx" + +class Laplacian; +class Field2D; +class Field3D; +class FieldPerp; +class Mesh; + +/// Helper class to do FFTs for `LaplaceCyclic`, `LaplacePCR`, `LaplacePCR_THOMAS` +class FFTTransform { + /// Physical length of the Z domain + BoutReal zlength; + /// Number of unfiltered Fourier modes + int nmode; + + int xs; + int xe; + int ys; + int ye; + int zs; + int ze; + + int nx; + int ny; + /// Number of (interior) points in Z + int nz; + int nxny; + /// Number of systems to solve = number of unfiltered Fourier modes times number of y + /// points + int nsys; + /// Number of inner boundary cells + int inbndry; + /// Number of outer boundary cells + int outbndry; + + bool inner_boundary_set_on_first_x; + bool outer_boundary_set_on_last_x; + + bool zero_DC; + + const Mesh* localmesh; + +public: + FFTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, int ye, int zs, + int ze, int inbndry, int outbndry, bool inner_boundary_set_on_first_x, + bool outer_boundary_set_on_last_x, bool zero_DC); + + struct Matrices { + Matrix a; + Matrix b; + Matrix c; + Matrix bcmplx; + + Matrices(int nsys, int nx) + : a(nsys, nx), b(nsys, nx), c(nsys, nx), bcmplx(nsys, nx) {} + }; + + auto forward(const Laplacian& laplacian, const Field3D& rhs, const Field3D& x0, + const Field2D& Acoef, const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices; + auto backward(const Field3D& rhs, const Matrix& xcmplx3D) const -> Field3D; + + auto forward(const Laplacian& laplacian, const FieldPerp& rhs, const FieldPerp& x0, + const Field2D& Acoef, const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices; + auto backward(const FieldPerp& rhs, const Matrix& xcmplx) const -> FieldPerp; +}; + +/// Helper class to do discrete sin transforms (DSTs) for `LaplaceCyclic`, +/// `LaplacePCR`, `LaplacePCR_THOMAS` +class DSTTransform { + /// Physical length of the Z domain + BoutReal zlength; + /// Number of unfiltered Fourier modes + int nmode; + + int xs; + int xe; + int ys; + int ye; + int zs; + int ze; + + int nx; + int ny; + /// Number of (interior) points in Z + int nz; + int nxny; + /// Number of systems to solve = number of unfiltered Fourier modes times number of y + /// points + int nsys; + /// Number of inner boundary cells + int inbndry; + /// Number of outer boundary cells + int outbndry; + + bool inner_boundary_set_on_first_x; + bool outer_boundary_set_on_last_x; + + bool zero_DC; + + const Mesh* localmesh; + +public: + DSTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, int ye, int zs, + int ze, int inbndry, int outbndry, bool inner_boundary_set_on_first_x, + bool outer_boundary_set_on_last_x, bool zero_DC); + + struct Matrices { + Matrix a; + Matrix b; + Matrix c; + Matrix bcmplx; + + Matrices(int nsys, int nx) + : a(nsys, nx), b(nsys, nx), c(nsys, nx), bcmplx(nsys, nx) {} + }; + + auto forward(const Laplacian& laplacian, const Field3D& rhs, const Field3D& x0, + const Field2D& Acoef, const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices; + auto backward(const Field3D& rhs, const Matrix& xcmplx3D) const -> Field3D; + + auto forward(const Laplacian& laplacian, const FieldPerp& rhs, const FieldPerp& x0, + const Field2D& Acoef, const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices; + auto backward(const FieldPerp& rhs, const Matrix& xcmplx) const -> FieldPerp; +}; diff --git a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx index d726b31644..22a2d8899a 100644 --- a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx +++ b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx @@ -25,8 +25,14 @@ #if not BOUT_USE_METRIC_3D #include "cyclic_laplace.hxx" -#include "bout/assert.hxx" -#include "bout/bout_types.hxx" + +#include "../../common_transform.hxx" + +#include "bout/array.hxx" +#include "bout/dcomplex.hxx" +#include +#include +#include #include #include #include @@ -36,12 +42,15 @@ #include #include +#include #include LaplaceCyclic::LaplaceCyclic(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), Acoef(0.0), C1coef(1.0), C2coef(1.0), Dcoef(1.0) { + bout::fft::assertZSerial(*localmesh, "`cyclic` inversion"); + Acoef.setLocation(location); C1coef.setLocation(location); C2coef.setLocation(location); @@ -100,15 +109,11 @@ FieldPerp LaplaceCyclic::solve(const FieldPerp& rhs, const FieldPerp& x0) { ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); - FieldPerp x{emptyFrom(rhs)}; // Result - - int jy = rhs.getIndex(); // Get the Y index - x.setIndex(jy); - // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set - int inbndry = localmesh->xstart, outbndry = localmesh->xstart; + int inbndry = localmesh->xstart; + int outbndry = localmesh->xstart; if (isGlobalFlagSet(INVERT_BOTH_BNDRY_ONE) || (localmesh->xstart < 2)) { inbndry = outbndry = 1; } @@ -120,176 +125,53 @@ FieldPerp LaplaceCyclic::solve(const FieldPerp& rhs, const FieldPerp& x0) { } if (dst) { - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells. (unless periodic - // in x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Get elements of the tridiagonal matrix - // including boundary conditions - BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - // wave number is 1/[rad]; DST has extra 2. - BoutReal kwave = kz * 2.0 * PI / (2. * zlen); - - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false, // Don't include guard cells in arrays - false); // Z domain not periodic - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems cr->setCoefs(a, b, c); cr->solve(bcmplx, xcmplx); - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - - // ZFFT routine expects input of this length - auto k1d = Array(localmesh->LocalNz); - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } - - for (int kz = nmode; kz < (localmesh->LocalNz); kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, x[ix] + 1); - - x(ix, 0) = -x(ix, 2); - x(ix, localmesh->LocalNz - 1) = -x(ix, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of this length - auto k1d = Array((localmesh->LocalNz) / 2 + 1); - - // Loop over X indices, including boundaries but not guard - // cells (unless periodic in x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0[ix], localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs[ix], localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } - - // Solve tridiagonal systems - cr->setCoefs(a, b, c); - cr->solve(bcmplx, xcmplx); + return transform.backward(rhs, xcmplx); + } - if (localmesh->periodicX) { - // Subtract X average of kz=0 mode - BoutReal local[2] = { - 0.0, // index 0 = sum of coefficients - static_cast(xe - xs + 1) // number of grid cells - }; - for (int ix = xs; ix <= xe; ix++) { - local[0] += xcmplx(0, ix - xs).real(); - } - BoutReal global[2]; - MPI_Allreduce(local, global, 2, MPI_DOUBLE, MPI_SUM, localmesh->getXcomm()); - BoutReal avg = global[0] / global[1]; - for (int ix = xs; ix <= xe; ix++) { - xcmplx(0, ix - xs) -= avg; - } + const FFTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); + + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); + + // Solve tridiagonal systems + cr->setCoefs(matrices.a, matrices.b, matrices.c); + cr->solve(matrices.bcmplx, xcmplx); + + if (localmesh->periodicX) { + // Subtract X average of kz=0 mode + std::array local = { + 0.0, // index 0 = sum of coefficients + static_cast(xe - xs + 1) // number of grid cells + }; + for (int ix = xs; ix <= xe; ix++) { + local[0] += xcmplx(0, ix - xs).real(); } - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of this length - auto k1d = Array((localmesh->LocalNz) / 2 + 1); - - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - if (zero_DC) { - k1d[0] = 0.; - } - - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } - - for (int kz = nmode; kz < (localmesh->LocalNz) / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - irfft(std::begin(k1d), localmesh->LocalNz, x[ix]); - } + std::array global{}; + MPI_Allreduce(local.data(), global.data(), 2, MPI_DOUBLE, MPI_SUM, + localmesh->getXcomm()); + const BoutReal avg = global[0] / global[1]; + for (int ix = xs; ix <= xe; ix++) { + xcmplx(0, ix - xs) -= avg; } } - checkData(x); - - return x; + return transform.backward(rhs, xcmplx); } Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { - TRACE("LaplaceCyclic::solve(Field3D, Field3D)"); ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); @@ -302,7 +184,8 @@ Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set - int inbndry = localmesh->xstart, outbndry = localmesh->xstart; + int inbndry = localmesh->xstart; + int outbndry = localmesh->xstart; if (isGlobalFlagSet(INVERT_BOTH_BNDRY_ONE) || (localmesh->xstart < 2)) { inbndry = outbndry = 1; } @@ -316,7 +199,8 @@ Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { int nx = xe - xs + 1; // Number of X points on this processor // Get range of Y indices - int ys = localmesh->ystart, ye = localmesh->yend; + int ys = localmesh->ystart; + int ye = localmesh->yend; if (localmesh->hasBndryLowerY()) { if (include_yguards) { @@ -335,215 +219,61 @@ Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { const int ny = (ye - ys + 1); // Number of Y points const int nsys = nmode * ny; // Number of systems of equations to solve - const int nxny = nx * ny; // Number of points in X-Y // This is just to silence static analysis ASSERT0(ny > 0); - auto a3D = Matrix(nsys, nx); - auto b3D = Matrix(nsys, nx); - auto c3D = Matrix(nsys, nx); - auto xcmplx3D = Matrix(nsys, nx); - auto bcmplx3D = Matrix(nsys, nx); if (dst) { - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of this length - auto k1d = Array(localmesh->LocalNz); - - // Loop over X and Y indices, including boundaries but not guard cells. - // (unless periodic in x) - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Get elements of the tridiagonal matrix - // including boundary conditions - const BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - // wave number is 1/[rad]; DST has extra 2. - BoutReal kwave = kz * 2.0 * PI / (2. * zlen); - - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false, // Don't include guard cells in arrays - false); // Z domain not periodic - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - cr->setCoefs(a3D, b3D, c3D); - cr->solve(bcmplx3D, xcmplx3D); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of length LocalNz - auto k1d = Array(localmesh->LocalNz); - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } - - for (int kz = nmode; kz < localmesh->LocalNz; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, &x(ix, iy, 1)); - - x(ix, iy, 0) = -x(ix, iy, 2); - x(ix, iy, localmesh->LocalNz - 1) = -x(ix, iy, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of this length - auto k1d = Array(localmesh->LocalNz / 2 + 1); - - // Loop over X and Y indices, including boundaries but not guard cells - // (unless periodic in x) - - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } + cr->setCoefs(matrices.a, matrices.b, matrices.c); + cr->solve(matrices.bcmplx, xcmplx3D); - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } - - // Solve tridiagonal systems - cr->setCoefs(a3D, b3D, c3D); - cr->solve(bcmplx3D, xcmplx3D); - - if (localmesh->periodicX) { - // Subtract X average of kz=0 mode - std::vector local(ny + 1, 0.0); - for (int y = 0; y < ny; y++) { - for (int ix = xs; ix <= xe; ix++) { - local[y] += xcmplx3D(y * nmode, ix - xs).real(); - } - } - local[ny] = static_cast(xe - xs + 1); - - // Global reduce - std::vector global(ny + 1, 0.0); - MPI_Allreduce(local.data(), global.data(), ny + 1, MPI_DOUBLE, MPI_SUM, - localmesh->getXcomm()); - // Subtract average from kz=0 modes - for (int y = 0; y < ny; y++) { - BoutReal avg = global[y] / global[ny]; - for (int ix = xs; ix <= xe; ix++) { - xcmplx3D(y * nmode, ix - xs) -= avg; - } - } - } - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - if (zero_DC) { - k1d[0] = 0.; - } + return transform.backward(rhs, xcmplx3D); + } + const FFTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - for (int kz = nmode; kz < localmesh->LocalNz / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } + // Solve tridiagonal systems + cr->setCoefs(matrices.a, matrices.b, matrices.c); + cr->solve(matrices.bcmplx, xcmplx3D); - irfft(std::begin(k1d), localmesh->LocalNz, x(ix, iy)); + if (localmesh->periodicX) { + // Subtract X average of kz=0 mode + std::vector local(ny + 1, 0.0); + for (int y = 0; y < ny; y++) { + for (int ix = xs; ix <= xe; ix++) { + local[y] += xcmplx3D(y * nmode, ix - xs).real(); + } + } + local[ny] = static_cast(xe - xs + 1); + + // Global reduce + std::vector global(ny + 1, 0.0); + MPI_Allreduce(local.data(), global.data(), ny + 1, MPI_DOUBLE, MPI_SUM, + localmesh->getXcomm()); + // Subtract average from kz=0 modes + for (int y = 0; y < ny; y++) { + BoutReal avg = global[y] / global[ny]; + for (int ix = xs; ix <= xe; ix++) { + xcmplx3D(y * nmode, ix - xs) -= avg; } } } - checkData(x); - - return x; + return transform.backward(rhs, xcmplx3D); } void LaplaceCyclic ::verify_solution(const Matrix& a_ver, @@ -578,21 +308,21 @@ void LaplaceCyclic ::verify_solution(const Matrix& a_ver, } if (xproc > 0) { - MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, MPI_COMM_WORLD, + MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, BoutComm::get(), &request[1]); for (int kz = 0; kz < nsys; kz++) { sbufdown[kz] = x_ver(kz, 1); } - MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, MPI_COMM_WORLD, + MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, BoutComm::get(), &request[0]); } if (xproc < nprocs - 1) { - MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, MPI_COMM_WORLD, + MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, BoutComm::get(), &request[3]); for (int kz = 0; kz < nsys; kz++) { sbufup[kz] = x_ver(kz, nx); } - MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, MPI_COMM_WORLD, + MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, BoutComm::get(), &request[2]); } diff --git a/src/invert/laplace/impls/cyclic/cyclic_laplace.hxx b/src/invert/laplace/impls/cyclic/cyclic_laplace.hxx index 6aa9a4eb19..00b9ad01af 100644 --- a/src/invert/laplace/impls/cyclic/cyclic_laplace.hxx +++ b/src/invert/laplace/impls/cyclic/cyclic_laplace.hxx @@ -1,6 +1,6 @@ /*!\file * Perpendicular Laplacian inversion in serial or parallel - * + * * Simplified version of serial_tri and spt algorithms. Uses FFTs and * the cyclic reduction class to solve tridiagonal systems. */ @@ -8,7 +8,7 @@ * Copyright 2013 B.D.Dudson * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify diff --git a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx index 5255ea939d..e9219091e2 100644 --- a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx +++ b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx @@ -67,12 +67,14 @@ LaplaceIPT::LaplaceIPT(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED au(ny, nmode), bu(ny, nmode), rl(nmode), ru(nmode), r1(ny, nmode), r2(ny, nmode), first_call(ny), x0saved(ny, 4, nmode), converged(nmode), fine_error(4, nmode) { + bout::fft::assertZSerial(*localmesh, "`ipt` inversion"); + A.setLocation(location); C.setLocation(location); D.setLocation(location); // Number of procs must be a factor of 2 - const int n = localmesh->NXPE; + const int n = localmesh->getNXPE(); if (!is_pow2(n)) { throw BoutException("LaplaceIPT error: NXPE must be a power of 2"); } @@ -234,7 +236,6 @@ FieldPerp LaplaceIPT::solve(const FieldPerp& b, const FieldPerp& x0) { ASSERT1(localmesh == b.getMesh() && localmesh == x0.getMesh()); ASSERT1(b.getLocation() == location); ASSERT1(x0.getLocation() == location); - TRACE("LaplaceIPT::solve(const, const)"); FieldPerp x{emptyFrom(b)}; diff --git a/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx b/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx index 04c8c7e0c9..4be5bfbad3 100644 --- a/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx +++ b/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx @@ -33,7 +33,6 @@ #if not BOUT_USE_METRIC_3D #include -#include #include #if BOUT_USE_OPENMP @@ -46,8 +45,6 @@ LaplaceMultigrid::LaplaceMultigrid(Options* opt, const CELL_LOC loc, Mesh* mesh_ Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), A(0.0), C1(1.0), C2(1.0), D(1.0) { - TRACE("LaplaceMultigrid::LaplaceMultigrid(Options *opt)"); - // periodic x-direction not handled: see MultigridAlg::communications ASSERT1(!localmesh->periodicX); @@ -223,8 +220,6 @@ LaplaceMultigrid::LaplaceMultigrid(Options* opt, const CELL_LOC loc, Mesh* mesh_ FieldPerp LaplaceMultigrid::solve(const FieldPerp& b_in, const FieldPerp& x0) { - TRACE("LaplaceMultigrid::solve(const FieldPerp, const FieldPerp)"); - ASSERT1(localmesh == b_in.getMesh() && localmesh == x0.getMesh()); ASSERT1(b_in.getLocation() == location); ASSERT1(x0.getLocation() == location); @@ -578,7 +573,6 @@ FieldPerp LaplaceMultigrid::solve(const FieldPerp& b_in, const FieldPerp& x0) { } void LaplaceMultigrid::generateMatrixF(int level) { - TRACE("LaplaceMultigrid::generateMatrixF(int)"); // Set (fine-level) matrix entries diff --git a/src/invert/laplace/impls/naulin/naulin_laplace.cxx b/src/invert/laplace/impls/naulin/naulin_laplace.cxx index 7168d3878e..3faba2f0ea 100644 --- a/src/invert/laplace/impls/naulin/naulin_laplace.cxx +++ b/src/invert/laplace/impls/naulin/naulin_laplace.cxx @@ -1,7 +1,26 @@ -/************************************************************************** - * Copyright 2018 B.D.Dudson, M. Loiten, J. Omotani +// clang-format off +/*! + * \file naulin_laplace.cxx * - * Contact: Ben Dudson, benjamin.dudson@york.ac.uk + * \brief Iterative solver to handle non-constant-in-z coefficients + * + * Scheme suggested by Volker Naulin: solve + * Delp2(phi[i+1]) + 1/DC(C1*D)*Grad_perp(DC(C2))*Grad_perp(phi[i+1]) + DC(A/D)*phi[i+1] + * = rhs(phi[i]) + 1/DC(C1*D)*Grad_perp(DC(C2))*Grad_perp(phi[i]) + DC(A/D)*phi[i] + * using standard FFT-based solver, iterating to include other terms by + * evaluating them on rhs using phi from previous iteration. + * DC part (i.e. Field2D part) of C1*D, C2 and A/D is kept in the FFT inversion + * to improve convergence by including as much as possible in the direct solve + * and so that all Neumann boundary conditions can be used at least when + * DC(A/D)!=0. + * + * CHANGELOG + * ========= + * + ************************************************************************** + * Copyright 2018 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -20,11 +39,16 @@ * */ +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include #include #include #include #include +#include #include #include @@ -85,7 +109,7 @@ LaplaceNaulin::LaplaceNaulin(Options* opt, const CELL_LOC loc, Mesh* mesh_in, ASSERT0(underrelax_recovery >= 1.); delp2solver = create(opt->getSection("delp2solver"), location, localmesh); std::string delp2type; - opt->getSection("delp2solver")->get("type", delp2type, "cyclic"); + opt->getSection("delp2solver")->get("type", delp2type, LaplaceFactory::default_type); // Check delp2solver is using an FFT scheme, otherwise it will not exactly // invert Delp2 and we will not converge ASSERT0(delp2type == "cyclic" || delp2type == "spt" || delp2type == "tri"); @@ -284,7 +308,7 @@ void LaplaceNaulin::copy_x_boundaries(Field3D& x, const Field3D& x0, Mesh* local if (localmesh->firstX()) { for (int i = localmesh->xstart - 1; i >= 0; --i) { for (int j = localmesh->ystart; j <= localmesh->yend; ++j) { - for (int k = 0; k < localmesh->LocalNz; ++k) { + for (int k = localmesh->zstart; k <= localmesh->zend; ++k) { x(i, j, k) = x0(i, j, k); } } @@ -293,7 +317,7 @@ void LaplaceNaulin::copy_x_boundaries(Field3D& x, const Field3D& x0, Mesh* local if (localmesh->lastX()) { for (int i = localmesh->xend + 1; i < localmesh->LocalNx; ++i) { for (int j = localmesh->ystart; j <= localmesh->yend; ++j) { - for (int k = 0; k < localmesh->LocalNz; ++k) { + for (int k = localmesh->zstart; k <= localmesh->zend; ++k) { x(i, j, k) = x0(i, j, k); } } @@ -308,3 +332,5 @@ void LaplaceNaulin::outputVars(Options& output_options, output_options[fmt::format("{}_mean_underrelax_counts", getPerformanceName())] .assignRepeat(naulinsolver_mean_underrelax_counts, time_dimension); } + +#endif diff --git a/src/invert/laplace/impls/naulin/naulin_laplace.hxx b/src/invert/laplace/impls/naulin/naulin_laplace.hxx index 57b4ac6f62..edcc562c99 100644 --- a/src/invert/laplace/impls/naulin/naulin_laplace.hxx +++ b/src/invert/laplace/impls/naulin/naulin_laplace.hxx @@ -124,7 +124,7 @@ * Copyright 2018 B.D.Dudson, M. Loiten, J. Omotani * * Contact: Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -147,16 +147,27 @@ class LaplaceNaulin; #ifndef BOUT_LAP_NAULIN_H #define BOUT_LAP_NAULIN_H +#include #include + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacenaulin(LAPLACE_NAULIN, "BOUT++ was configured with 3D metrics"); +} + +#else + #include namespace { -RegisterLaplace registerlaplacenaulin(LAPLACE_NAULIN); +const RegisterLaplace registerlaplacenaulin(LAPLACE_NAULIN); } /// Solves the 2D Laplacian equation /*! - * + * */ class LaplaceNaulin : public Laplacian { public: @@ -317,4 +328,6 @@ private: void copy_x_boundaries(Field3D& x, const Field3D& x0, Mesh* mesh); }; +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_LAP_NAULIN_H diff --git a/src/invert/laplace/impls/pcr/pcr.cxx b/src/invert/laplace/impls/pcr/pcr.cxx index 48bbdbac4b..93fbf09cdb 100644 --- a/src/invert/laplace/impls/pcr/pcr.cxx +++ b/src/invert/laplace/impls/pcr/pcr.cxx @@ -36,23 +36,28 @@ * **************************************************************************/ +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include "pcr.hxx" -#include "bout/globals.hxx" +#include "../../common_transform.hxx" + +#include +#include #include #include +#include #include +#include #include #include #include -#include -#include -#include - -#include "bout/boutcomm.hxx" #include - #include +#include +#include #include #include @@ -67,6 +72,8 @@ LaplacePCR::LaplacePCR(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED ncx(localmesh->LocalNx), ny(localmesh->LocalNy), avec(ny, nmode, ncx), bvec(ny, nmode, ncx), cvec(ny, nmode, ncx) { + bout::fft::assertZSerial(*localmesh, "`pcr` inversion"); + Acoef.setLocation(location); C1coef.setLocation(location); C2coef.setLocation(location); @@ -160,155 +167,32 @@ FieldPerp LaplacePCR::solve(const FieldPerp& rhs, const FieldPerp& x0) { } if (dst) { - const BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells. (unless periodic - // in x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - BoutReal kwave = - kz * 2.0 * PI / (2. * zlen); // wave number is 1/[rad]; DST has extra 2. - - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } // BOUT_OMP_PERF(parallel) + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - cr_pcr_solver(a, b, c, bcmplx, xcmplx); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } + cr_pcr_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx); - for (int kz = nmode; kz < (localmesh->LocalNz); kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, x[ix] + 1); - - x(ix, 0) = -x(ix, 2); - x(ix, localmesh->LocalNz - 1) = -x(ix, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells (unless periodic in - // x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0[ix], localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs[ix], localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } // BOUT_OMP_PERF(parallel) - - // Solve tridiagonal systems - cr_pcr_solver(a, b, c, bcmplx, xcmplx); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - if (zero_DC) { - k1d[0] = 0.; - } - - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } - - for (int kz = nmode; kz < (localmesh->LocalNz) / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - irfft(std::begin(k1d), localmesh->LocalNz, x[ix]); - } - } + return transform.backward(rhs, xcmplx); } + const FFTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); + + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - checkData(x); + // Solve tridiagonal systems + cr_pcr_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx); - return x; + return transform.backward(rhs, xcmplx); } Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { - TRACE("LaplacePCR::solve(Field3D, Field3D)"); ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); @@ -316,8 +200,6 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { Timer timer("invert"); - Field3D x{emptyFrom(rhs)}; // Result - // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set @@ -356,185 +238,33 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { const int ny = (ye - ys + 1); // Number of Y points nsys = nmode * ny; // Number of systems of equations to solve - const int nxny = nx * ny; // Number of points in X-Y - - auto a3D = Matrix(nsys, nx); - auto b3D = Matrix(nsys, nx); - auto c3D = Matrix(nsys, nx); auto xcmplx3D = Matrix(nsys, nx); - auto bcmplx3D = Matrix(nsys, nx); if (dst) { - const BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X and Y indices, including boundaries but not guard cells. - // (unless periodic in x) - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - BoutReal kwave = - kz * 2.0 * PI / (2. * zlen); // wave number is 1/[rad]; DST has extra 2. - - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } // BOUT_OMP_PERF(parallel) - - // Solve tridiagonal systems - cr_pcr_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } - - for (int kz = nmode; kz < localmesh->LocalNz; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, &x(ix, iy, 1)); - - x(ix, iy, 0) = -x(ix, iy, 2); - x(ix, iy, localmesh->LocalNz - 1) = -x(ix, iy, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array(localmesh->LocalNz / 2 - + 1); // ZFFT routine expects input of this length - - // Loop over X and Y indices, including boundaries but not guard cells - // (unless periodic in x) - - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } // BOUT_OMP_PERF(parallel) + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - cr_pcr_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - if (zero_DC) { - k1d[0] = 0.; - } - - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } - - for (int kz = nmode; kz < localmesh->LocalNz / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } + cr_pcr_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx3D); - irfft(std::begin(k1d), localmesh->LocalNz, x(ix, iy)); - } - } + return transform.backward(rhs, xcmplx3D); } + const FFTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); + + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - checkData(x); + // Solve tridiagonal systems + cr_pcr_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx3D); - return x; + return transform.backward(rhs, xcmplx3D); } /** @@ -555,6 +285,8 @@ void LaplacePCR ::cr_pcr_solver(Matrix& a_mpi, Matrix& b_mpi const int xend = localmesh->xend; const int nx = xend - xstart + 1; // number of interior points + const int nsys = std::get<0>(a_mpi.shape()); + // Handle boundary points so that the PCR algorithm works with arrays of // the same size on each rank. // Note that this modifies the coefficients of b and r in the first and last @@ -621,6 +353,8 @@ void LaplacePCR ::cr_pcr_solver(Matrix& a_mpi, Matrix& b_mpi void LaplacePCR ::eliminate_boundary_rows(Matrix& a, Matrix& b, Matrix& c, Matrix& r) { + const int nsys = std::get<0>(a.shape()); + if (localmesh->firstX()) { for (int kz = 0; kz < nsys; kz++) { // Do forward elimination on *all* boundary rows up to xstart @@ -656,6 +390,8 @@ void LaplacePCR ::apply_boundary_conditions(const Matrix& a, const Matrix& r, Matrix& x) { + const int nsys = std::get<0>(a.shape()); + if (localmesh->firstX()) { for (int kz = 0; kz < nsys; kz++) { for (int ix = localmesh->xstart - 1; ix >= 0; ix--) { @@ -681,6 +417,8 @@ void LaplacePCR ::apply_boundary_conditions(const Matrix& a, void LaplacePCR ::cr_forward_multiple_row(Matrix& a, Matrix& b, Matrix& c, Matrix& r) const { + const int nsys = std::get<0>(a.shape()); + MPI_Comm comm = BoutComm::get(); Array alpha(nsys); Array gamma(nsys); @@ -754,6 +492,8 @@ void LaplacePCR ::cr_forward_multiple_row(Matrix& a, Matrix& void LaplacePCR ::cr_backward_multiple_row(Matrix& a, Matrix& b, Matrix& c, Matrix& r, Matrix& x) const { + const int nsys = std::get<0>(a.shape()); + MPI_Comm comm = BoutComm::get(); MPI_Status status; @@ -805,6 +545,8 @@ void LaplacePCR ::pcr_forward_single_row(Matrix& a, Matrix& Matrix& c, Matrix& r, Matrix& x) const { + const int nsys = std::get<0>(a.shape()); + Array alpha(nsys); Array gamma(nsys); Array sbuf(4 * nsys); @@ -818,7 +560,6 @@ void LaplacePCR ::pcr_forward_single_row(Matrix& a, Matrix& const int nlevel = log2(nprocs); const int nhprocs = nprocs / 2; int dist_rank = 1; - int dist2_rank = 2; /// Parallel cyclic reduction continues until 2x2 matrix are made between a pair of /// rank, (myrank, myrank+nhprocs). @@ -936,7 +677,6 @@ void LaplacePCR ::pcr_forward_single_row(Matrix& a, Matrix& } dist_rank *= 2; - dist2_rank *= 2; } /// Solving 2x2 matrix. All pair of ranks, myrank and myrank+nhprocs, solves it @@ -1041,21 +781,21 @@ void LaplacePCR ::verify_solution(const Matrix& a_ver, } if (xproc > 0) { - MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, MPI_COMM_WORLD, + MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, BoutComm::get(), &request[1]); for (int kz = 0; kz < nsys; kz++) { sbufdown[kz] = x_ver(kz, 1); } - MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, MPI_COMM_WORLD, + MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, BoutComm::get(), &request[0]); } if (xproc < nprocs - 1) { - MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, MPI_COMM_WORLD, + MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, BoutComm::get(), &request[3]); for (int kz = 0; kz < nsys; kz++) { sbufup[kz] = x_ver(kz, nx); } - MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, MPI_COMM_WORLD, + MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, BoutComm::get(), &request[2]); } @@ -1107,3 +847,5 @@ void LaplacePCR ::verify_solution(const Matrix& a_ver, output.write("max abs error {}\n", max_error); output.write("max abs error location {} {}\n", max_loc_x, max_loc_z); } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/pcr/pcr.hxx b/src/invert/laplace/impls/pcr/pcr.hxx index f1c6431bd4..bad57cb92b 100644 --- a/src/invert/laplace/impls/pcr/pcr.hxx +++ b/src/invert/laplace/impls/pcr/pcr.hxx @@ -29,13 +29,24 @@ class LaplacePCR; #ifndef BOUT_PCR_H #define BOUT_PCR_H -#include +#include #include + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacepcr(LAPLACE_PCR, "BOUT++ was configured with 3D metrics"); +} + +#else + +#include #include #include namespace { -RegisterLaplace registerlaplacepcr(LAPLACE_PCR); +const RegisterLaplace registerlaplacepcr(LAPLACE_PCR); } class LaplacePCR : public Laplacian { @@ -175,4 +186,6 @@ private: bool dst{false}; }; +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_PCR_H diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx index 61c8f58694..84ff871ed0 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx @@ -36,10 +36,18 @@ * **************************************************************************/ +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include "pcr_thomas.hxx" -#include "bout/globals.hxx" +#include "../../common_transform.hxx" + +#include "bout/array.hxx" #include "bout/boutcomm.hxx" +#include "bout/dcomplex.hxx" +#include "bout/globals.hxx" #include #include #include @@ -65,6 +73,8 @@ LaplacePCR_THOMAS::LaplacePCR_THOMAS(Options* opt, CELL_LOC loc, Mesh* mesh_in, ncx(localmesh->LocalNx), ny(localmesh->LocalNy), avec(ny, nmode, ncx), bvec(ny, nmode, ncx), cvec(ny, nmode, ncx) { + bout::fft::assertZSerial(*localmesh, "`pcr_thomas` inversion"); + Acoef.setLocation(location); C1coef.setLocation(location); C2coef.setLocation(location); @@ -135,11 +145,6 @@ FieldPerp LaplacePCR_THOMAS::solve(const FieldPerp& rhs, const FieldPerp& x0) { ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); - FieldPerp x{emptyFrom(rhs)}; // Result - - int jy = rhs.getIndex(); // Get the Y index - x.setIndex(jy); - // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set @@ -156,155 +161,33 @@ FieldPerp LaplacePCR_THOMAS::solve(const FieldPerp& rhs, const FieldPerp& x0) { } if (dst) { - const BoutReal zlength = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells. (unless periodic - // in x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - // wave number is 1/[rad]; DST has extra 2. - const BoutReal kwave = kz * 2.0 * PI / (2. * zlength); - - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - pcr_thomas_solver(a, b, c, bcmplx, xcmplx); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } - - for (int kz = nmode; kz < (localmesh->LocalNz); kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } + pcr_thomas_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx); - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, x[ix] + 1); - - x(ix, 0) = -x(ix, 2); - x(ix, localmesh->LocalNz - 1) = -x(ix, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells (unless periodic in - // x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0[ix], localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs[ix], localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } - - // Solve tridiagonal systems - pcr_thomas_solver(a, b, c, bcmplx, xcmplx); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - if (zero_DC) { - k1d[0] = 0.; - } - - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } + return transform.backward(rhs, xcmplx); + } - for (int kz = nmode; kz < (localmesh->LocalNz) / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } + const FFTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - irfft(std::begin(k1d), localmesh->LocalNz, x[ix]); - } - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - checkData(x); + // Solve tridiagonal systems + pcr_thomas_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx); - return x; + return transform.backward(rhs, xcmplx); } Field3D LaplacePCR_THOMAS::solve(const Field3D& rhs, const Field3D& x0) { - TRACE("LaplacePCR_THOMAS::solve(Field3D, Field3D)"); ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); @@ -312,8 +195,6 @@ Field3D LaplacePCR_THOMAS::solve(const Field3D& rhs, const Field3D& x0) { Timer timer("invert"); - Field3D x{emptyFrom(rhs)}; // Result - // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set @@ -329,7 +210,7 @@ Field3D LaplacePCR_THOMAS::solve(const Field3D& rhs, const Field3D& x0) { outbndry = 1; } - int nx = xe - xs + 1; // Number of X points on this processor + const int nx = xe - xs + 1; // Number of X points on this processor // Get range of Y indices int ys = localmesh->ystart; @@ -352,186 +233,33 @@ Field3D LaplacePCR_THOMAS::solve(const Field3D& rhs, const Field3D& x0) { const int ny = (ye - ys + 1); // Number of Y points nsys = nmode * ny; // Number of systems of equations to solve - const int nxny = nx * ny; // Number of points in X-Y - - auto a3D = Matrix(nsys, nx); - auto b3D = Matrix(nsys, nx); - auto c3D = Matrix(nsys, nx); - auto xcmplx3D = Matrix(nsys, nx); - auto bcmplx3D = Matrix(nsys, nx); if (dst) { - const BoutReal zlength = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X and Y indices, including boundaries but not guard cells. - // (unless periodic in x) - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - // wave number is 1/[rad]; DST has extra 2. - BoutReal kwave = kz * 2.0 * PI / (2. * zlength); - - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } - - // Solve tridiagonal systems - pcr_thomas_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - for (int kz = nmode; kz < localmesh->LocalNz; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, &x(ix, iy, 1)); - - x(ix, iy, 0) = -x(ix, iy, 2); - x(ix, iy, localmesh->LocalNz - 1) = -x(ix, iy, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array(localmesh->LocalNz / 2 - + 1); // ZFFT routine expects input of this length - - // Loop over X and Y indices, including boundaries but not guard cells - // (unless periodic in x) - - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - // wave number is 1/[rad] - BoutReal kwave = kz * 2.0 * PI / zlength; - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - pcr_thomas_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); + pcr_thomas_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx3D); - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - int ix = xs + ind / ny; - int iy = ys + ind % ny; + return transform.backward(rhs, xcmplx3D); + } - if (zero_DC) { - k1d[0] = 0.; - } + const FFTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - for (int kz = nmode; kz < localmesh->LocalNz / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } + // Solve tridiagonal systems + pcr_thomas_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx3D); - irfft(std::begin(k1d), localmesh->LocalNz, x(ix, iy)); - } - } - } - - checkData(x); - - return x; + return transform.backward(rhs, xcmplx3D); } /** @@ -555,6 +283,8 @@ void LaplacePCR_THOMAS ::pcr_thomas_solver(Matrix& a_mpi, const int xend = localmesh->xend; const int nx = xend - xstart + 1; // number of interior points + const int nsys = std::get<0>(a_mpi.shape()); + // Handle boundary points so that the PCR algorithm works with arrays of // the same size on each rank. // Note that this modifies the coefficients of b and r in the first and last @@ -621,6 +351,8 @@ void LaplacePCR_THOMAS ::eliminate_boundary_rows(const Matrix& a, const Matrix& c, Matrix& r) { + const int nsys = std::get<0>(a.shape()); + if (localmesh->firstX()) { // x index is first interior row const int xstart = localmesh->xstart; @@ -655,6 +387,8 @@ void LaplacePCR_THOMAS ::apply_boundary_conditions(const Matrix& a, const Matrix& r, Matrix& x) { + const int nsys = std::get<0>(a.shape()); + if (localmesh->firstX()) { for (int kz = 0; kz < nsys; kz++) { for (int ix = localmesh->xstart - 1; ix >= 0; ix--) { @@ -680,6 +414,8 @@ void LaplacePCR_THOMAS ::apply_boundary_conditions(const Matrix& a, void LaplacePCR_THOMAS ::cr_forward_multiple_row(Matrix& a, Matrix& b, Matrix& c, Matrix& r) const { + const int nsys = std::get<0>(a.shape()); + MPI_Comm comm = BoutComm::get(); Array alpha(nsys); Array gamma(nsys); @@ -755,6 +491,8 @@ void LaplacePCR_THOMAS ::cr_backward_multiple_row(Matrix& a, Matrix& c, Matrix& r, Matrix& x) const { + const int nsys = std::get<0>(a.shape()); + MPI_Comm comm = BoutComm::get(); MPI_Status status; @@ -806,6 +544,8 @@ void LaplacePCR_THOMAS ::pcr_forward_single_row(Matrix& a, Matrix& c, Matrix& r, Matrix& x) const { + const int nsys = std::get<0>(a.shape()); + Array alpha(nsys); Array gamma(nsys); Array sbuf(4 * nsys); @@ -983,6 +723,8 @@ void LaplacePCR_THOMAS ::pThomas_forward_multiple_row(Matrix& a, Matrix& b, Matrix& c, Matrix& r) const { + const int nsys = std::get<0>(a.shape()); + for (int kz = 0; kz < nsys; kz++) { for (int i = 3; i <= n_mpi; i++) { const dcomplex alpha = -a(kz, i) / b(kz, i - 1); @@ -1014,6 +756,8 @@ void LaplacePCR_THOMAS ::pcr_double_row_substitution(Matrix& a, Matrix& c, Matrix& r, Matrix& x) { + const int nsys = std::get<0>(a.shape()); + Array alpha(nsys); Array gamma(nsys); Array sbuf(4 * nsys); @@ -1140,21 +884,21 @@ void LaplacePCR_THOMAS ::verify_solution(const Matrix& a_ver, } if (xproc > 0) { - MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, MPI_COMM_WORLD, + MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, BoutComm::get(), &request[1]); for (int kz = 0; kz < nsys; kz++) { sbufdown[kz] = x_ver(kz, 1); } - MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, MPI_COMM_WORLD, + MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, BoutComm::get(), &request[0]); } if (xproc < nprocs - 1) { - MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, MPI_COMM_WORLD, + MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, BoutComm::get(), &request[3]); for (int kz = 0; kz < nsys; kz++) { sbufup[kz] = x_ver(kz, nx); } - MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, MPI_COMM_WORLD, + MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, BoutComm::get(), &request[2]); } @@ -1189,3 +933,5 @@ void LaplacePCR_THOMAS ::verify_solution(const Matrix& a_ver, } output.write("max abs error {}\n", max_error); } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx index ab888f27a4..ee08854b02 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx @@ -29,8 +29,19 @@ class LaplacePCR_THOMAS; #ifndef BOUT_PCR_THOMAS_H #define BOUT_PCR_THOMAS_H +#include "bout/build_defines.hxx" +#include "bout/invert_laplace.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacepcrthomas(LAPLACE_PCR_THOMAS, "BOUT++ was configured with 3D metrics"); +} + +#else + #include -#include #include #include @@ -178,4 +189,6 @@ private: bool dst{false}; }; +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_PCR_THOMAS_H diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 5f417d4faa..89ba25405b 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -31,41 +31,140 @@ #include "petsc_laplace.hxx" #include +#include #include +#include +#include +#include +#include #include +#include #include +#include #include +#include #include #include -#define KSP_RICHARDSON "richardson" -#define KSP_CHEBYSHEV "chebyshev" -#define KSP_CG "cg" -#define KSP_GMRES "gmres" -#define KSP_TCQMR "tcqmr" -#define KSP_BCGS "bcgs" -#define KSP_CGS "cgs" -#define KSP_TFQMR "tfqmr" -#define KSP_CR "cr" -#define KSP_LSQR "lsqr" -#define KSP_BICG "bicg" -#define KSP_PREONLY "preonly" - -static PetscErrorCode laplacePCapply(PC pc, Vec x, Vec y) { +#include +#include +#include +#include + +namespace { +PetscErrorCode laplacePCapply(PC pc, Vec x, Vec y) { PetscFunctionBegin; // NOLINT LaplacePetsc* laplace = nullptr; - const int ierr = PCShellGetContext(pc, reinterpret_cast(&laplace)); // NOLINT - CHKERRQ(ierr); + CHKERRQ(PCShellGetContext(pc, reinterpret_cast(&laplace))); // NOLINT PetscFunctionReturn(laplace->precon(x, y)); // NOLINT } +auto set_stencil(const Mesh& localmesh, bool fourth_order) { + OperatorStencil stencil; + IndexOffset zero; + // Start with a square stencil 1-point wide + std::set offsets = { + // clang-format off + zero.xm().zp(), zero.zp(), zero.xp().zp(), + zero.xm(), zero, zero.xp(), + zero.xm().zm(), zero.zm(), zero.xp().zm(), + // clang-format on + }; + + if (fourth_order) { + // Add a square stencil 2-points wide + offsets.insert({ + // clang-format off + zero.xm(2).zp(2), zero.xm().zp(2), zero.zp(2), zero.xp().zp(2), zero.xp(2).zp(2), + zero.xm(2).zp(), zero.xp(2).zp(), + zero.xm(2), zero.xp(2), + zero.xm(2).zm(), zero.xp(2).zm(), + zero.xm(2).zm(2), zero.xm().zm(2), zero.zm(2), zero.xp().zm(2), zero.xp(2).zm(2), + // clang-format on + }); + } + + const std::vector offsetsVec(offsets.begin(), offsets.end()); + stencil.add( + [&localmesh](IndPerp ind) -> bool { + return (localmesh.xstart <= ind.x() && ind.x() <= localmesh.xend + and (localmesh.zstart <= ind.z() && ind.z() <= localmesh.zend)); + }, + offsetsVec); + + // Add inner X boundary + if (localmesh.firstX()) { + const auto first_boundary = localmesh.xstart - 1; + const auto second_boundary = localmesh.xstart - 2; + + if (fourth_order) { + stencil.add( + [first_boundary, second_boundary](IndPerp ind) -> bool { + const auto x = ind.x(); + return x == first_boundary or x == second_boundary; + }, + {zero, zero.xp(1), zero.xp(2), zero.xp(3), zero.xp(4)}); + } else { + stencil.add( + [first_boundary](IndPerp ind) -> bool { return ind.x() == first_boundary; }, + {zero, zero.xp()}); + } + } + // Add outer X boundary + if (localmesh.lastX()) { + const auto first_boundary = localmesh.xend + 1; + const auto second_boundary = localmesh.xend + 2; + + if (fourth_order) { + stencil.add( + [first_boundary, second_boundary](IndPerp ind) -> bool { + const auto x = ind.x(); + return x == first_boundary or x == second_boundary; + }, + {zero, zero.xm(1), zero.xm(2), zero.xm(3), zero.xm(4)}); + } else { + stencil.add( + [first_boundary](IndPerp ind) -> bool { return ind.x() == first_boundary; }, + {zero, zero.xm()}); + } + } + + stencil.add([]([[maybe_unused]] IndPerp ind) -> bool { return true; }, {zero}); + return stencil; +} +} // namespace + LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, - Solver* UNUSED(solver)) - : Laplacian(opt, loc, mesh_in), A(0.0), C1(1.0), C2(1.0), D(1.0), Ex(0.0), Ez(0.0), - issetD(false), issetC(false), issetE(false), - lib(opt == nullptr ? &(Options::root()["laplace"]) : opt) { + [[maybe_unused]] Solver* solver) + : Laplacian(opt, loc, mesh_in), A(0.0, mesh_in), C1(1.0, mesh_in), C2(1.0, mesh_in), + D(1.0, mesh_in), Ex(0.0, mesh_in), Ez(0.0, mesh_in), issetD(false), issetC(false), + issetE(false), comm(localmesh->getXZcomm()), + opts(opt == nullptr ? &(Options::root()["laplace"]) : opt), + // WARNING: only a few of these options actually make sense: see the + // PETSc documentation to work out which they are (possibly + // pbjacobi, sor might be useful choices?) + ksptype((*opts)["ksptype"].doc("KSP solver type").withDefault(KSPGMRES)), + pctype((*opts)["pctype"] + .doc("Preconditioner type. See the PETSc documentation for options") + .withDefault("none")), + richardson_damping_factor((*opts)["richardson_damping_factor"].withDefault(1.0)), + chebyshev_max((*opts)["chebyshev_max"].withDefault(100)), + chebyshev_min((*opts)["chebyshev_min"].withDefault(0.01)), + gmres_max_steps((*opts)["gmres_max_steps"].withDefault(30)), + rtol((*opts)["rtol"].doc("Relative tolerance for KSP solver").withDefault(1e-5)), + atol((*opts)["atol"].doc("Absolute tolerance for KSP solver").withDefault(1e-50)), + dtol((*opts)["dtol"].doc("Divergence tolerance for KSP solver").withDefault(1e5)), + maxits( + (*opts)["maxits"].doc("Maximum number of KSP iterations").withDefault(100000)), + direct((*opts)["direct"].doc("Use direct (LU) solver?").withDefault(false)), + fourth_order( + (*opts)["fourth_order"].doc("Use fourth order stencil").withDefault(false)), + indexer(std::make_shared>( + localmesh, set_stencil(*localmesh, fourth_order))), + operator2D(indexer), lib(opts) { + A.setLocation(location); C1.setLocation(location); C2.setLocation(location); @@ -73,13 +172,6 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Ex.setLocation(location); Ez.setLocation(location); - // Get Options in Laplace Section - if (!opt) { - opts = Options::getRoot()->getSection("laplace"); - } else { - opts = opt; - } - #if CHECK > 0 // Checking flags are set to something which is not implemented checkFlags(); @@ -91,229 +183,27 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, } #endif - // Get communicator for group of processors in X - all points in z-x plane for fixed y. - comm = localmesh->getXcomm(); - - // Need to determine local size to use based on prior parallelisation - // Coefficient values are stored only on local processors. - localN = (localmesh->xend - localmesh->xstart + 1) * (localmesh->LocalNz); - if (localmesh->firstX()) { - localN += - localmesh->xstart - * (localmesh->LocalNz); // If on first processor add on width of boundary region - } - if (localmesh->lastX()) { - localN += - localmesh->xstart - * (localmesh->LocalNz); // If on last processor add on width of boundary region - } - - // Calculate 'size' (the total number of points in physical grid) - if (bout::globals::mpi->MPI_Allreduce(&localN, &size, 1, MPI_INT, MPI_SUM, comm) - != MPI_SUCCESS) { - throw BoutException("Error in MPI_Allreduce during LaplacePetsc initialisation"); - } - - // Calculate total (physical) grid dimensions - meshz = localmesh->LocalNz; - meshx = size / meshz; - - // Create PETSc type of vectors for the solution and the RHS vector - VecCreate(comm, &xs); - VecSetSizes(xs, localN, size); - VecSetFromOptions(xs); - VecDuplicate(xs, &bs); - - // Get 4th order solver switch - opts->get("fourth_order", fourth_order, false); - - // Set size of (the PETSc) Matrix on each processor to localN x localN - MatCreate(comm, &MatA); - MatSetSizes(MatA, localN, localN, size, size); - MatSetFromOptions(MatA); - - /* Pre allocate memory - * nnz denotes an array containing the number of non-zeros in the various rows - * for - * d_nnz - The diagonal terms in the matrix - * o_nnz - The off-diagonal terms in the matrix (needed when running in - * parallel) - */ - PetscInt *d_nnz, *o_nnz; - PetscMalloc((localN) * sizeof(PetscInt), &d_nnz); - PetscMalloc((localN) * sizeof(PetscInt), &o_nnz); - if (fourth_order) { - // first and last 2*localmesh-LocalNz entries are the edge x-values that (may) have 'off-diagonal' components (i.e. on another processor) - if (localmesh->firstX() && localmesh->lastX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { - d_nnz[i] = 15; - d_nnz[localN - 1 - i] = 15; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { - d_nnz[i] = 20; - d_nnz[localN - 1 - i] = 20; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - } else if (localmesh->firstX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { - d_nnz[i] = 15; - d_nnz[localN - 1 - i] = 15; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 10; - } - for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { - d_nnz[i] = 20; - d_nnz[localN - 1 - i] = 20; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 5; - } - } else if (localmesh->lastX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { - d_nnz[i] = 15; - d_nnz[localN - 1 - i] = 15; - o_nnz[i] = 10; - o_nnz[localN - 1 - i] = 0; - } - for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { - d_nnz[i] = 20; - d_nnz[localN - 1 - i] = 20; - o_nnz[i] = 5; - o_nnz[localN - 1 - i] = 0; - } - } else { - for (int i = 0; i < localmesh->LocalNz; i++) { - d_nnz[i] = 15; - d_nnz[localN - 1 - i] = 15; - o_nnz[i] = 10; - o_nnz[localN - 1 - i] = 10; - } - for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { - d_nnz[i] = 20; - d_nnz[localN - 1 - i] = 20; - o_nnz[i] = 5; - o_nnz[localN - 1 - i] = 5; - } - } - - for (int i = 2 * (localmesh->LocalNz); i < localN - 2 * ((localmesh->LocalNz)); i++) { - d_nnz[i] = 25; - d_nnz[localN - 1 - i] = 25; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - - // Use d_nnz and o_nnz for preallocating the matrix - if (localmesh->firstX() && localmesh->lastX()) { - // Only one processor in X - MatSeqAIJSetPreallocation(MatA, 0, d_nnz); - } else { - MatMPIAIJSetPreallocation(MatA, 0, d_nnz, 0, o_nnz); - } - } else { - // first and last localmesh->LocalNz entries are the edge x-values that (may) have 'off-diagonal' components (i.e. on another processor) - if (localmesh->firstX() && localmesh->lastX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { - d_nnz[i] = 6; - d_nnz[localN - 1 - i] = 6; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - } else if (localmesh->firstX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { - d_nnz[i] = 6; - d_nnz[localN - 1 - i] = 6; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 3; - } - } else if (localmesh->lastX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { - d_nnz[i] = 6; - d_nnz[localN - 1 - i] = 6; - o_nnz[i] = 3; - o_nnz[localN - 1 - i] = 0; - } - } else { - for (int i = 0; i < localmesh->LocalNz; i++) { - d_nnz[i] = 6; - d_nnz[localN - 1 - i] = 6; - o_nnz[i] = 3; - o_nnz[localN - 1 - i] = 3; - } - } - - for (int i = localmesh->LocalNz; i < localN - (localmesh->LocalNz); i++) { - d_nnz[i] = 9; - d_nnz[localN - 1 - i] = 9; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - - // Use d_nnz and o_nnz for preallocating the matrix - if (localmesh->firstX() && localmesh->lastX()) { - MatSeqAIJSetPreallocation(MatA, 0, d_nnz); - } else { - MatMPIAIJSetPreallocation(MatA, 0, d_nnz, 0, o_nnz); - } - } - // Free the d_nnz and o_nnz arrays, as these are will not be used anymore - PetscFree(d_nnz); - PetscFree(o_nnz); - // Sets up the internal matrix data structures for the later use. - MatSetUp(MatA); - - // Declare KSP Context (abstract PETSc object that manages all Krylov methods) - KSPCreate(comm, &ksp); - - // Get KSP Solver Type (Generalizes Minimal RESidual is the default) - ksptype = (*opts)["ksptype"].doc("KSP solver type").withDefault(KSP_GMRES); - - // Get preconditioner type - // WARNING: only a few of these options actually make sense: see the - // PETSc documentation to work out which they are (possibly - // pbjacobi, sor might be useful choices?) - pctype = (*opts)["pctype"] - .doc("Preconditioner type. See the PETSc documentation for options") - .withDefault("none"); - // Let "user" be a synonym for "shell" if (pctype == "user") { pctype = PCSHELL; } - // Get Options specific to particular solver types - opts->get("richardson_damping_factor", richardson_damping_factor, 1.0, true); - opts->get("chebyshev_max", chebyshev_max, 100, true); - opts->get("chebyshev_min", chebyshev_min, 0.01, true); - opts->get("gmres_max_steps", gmres_max_steps, 30, true); - - // Get Tolerances for KSP solver - rtol = (*opts)["rtol"].doc("Relative tolerance for KSP solver").withDefault(1e-5); - atol = (*opts)["atol"].doc("Absolute tolerance for KSP solver").withDefault(1e-50); - dtol = (*opts)["dtol"].doc("Divergence tolerance for KSP solver").withDefault(1e5); - maxits = (*opts)["maxits"].doc("Maximum number of KSP iterations").withDefault(100000); - - // Get direct solver switch - direct = (*opts)["direct"].doc("Use direct (LU) solver?").withDefault(false); if (direct) { - output << endl - << "Using LU decompostion for direct solution of system" << endl - << endl; + output.write("\nUsing LU decompostion for direct solution of system\n"); } if (pctype == PCSHELL) { - rightprec = (*opts)["rightprec"].doc("Right preconditioning?").withDefault(true); // Options for preconditioner are in a subsection pcsolve = Laplacian::create(opts->getSection("precon")); } +} - // Ensure that the matrix is constructed first time - // coefchanged = true; - // lastflag = -1; +LaplacePetsc::~LaplacePetsc() { + if (ksp_initialised) { + KSPDestroy(&ksp); + } } FieldPerp LaplacePetsc::solve(const FieldPerp& b) { return solve(b, b); } @@ -336,7 +226,6 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b) { return solve(b, b); } * \returns sol The solution x of the problem Ax=b. */ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { - TRACE("LaplacePetsc::solve"); ASSERT1(localmesh == b.getMesh() && localmesh == x0.getMesh()); ASSERT1(b.getLocation() == location); @@ -346,406 +235,38 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { checkFlags(); #endif - int y = b.getIndex(); // Get the Y index - sol.setIndex(y); // Initialize the solution field. - sol = 0.; - - // Determine which row/columns of the matrix are locally owned - MatGetOwnershipRange(MatA, &Istart, &Iend); - - int i = Istart; // The row in the PETSc matrix + // Set member variable so that we can pass through to shell preconditioner if + // required + yindex = b.getIndex(); { - Timer timer("petscsetup"); + const Timer timer("petscsetup"); - // if ((fourth_order) && !(lastflag&INVERT_4TH_ORDER)) throw BoutException("Should not change INVERT_4TH_ORDER flag in LaplacePetsc: 2nd order and 4th order require different pre-allocation to optimize PETSc solver"); + const bool inner_X_neumann = isInnerBoundaryFlagSet(INVERT_AC_GRAD); + const bool outer_X_neumann = isOuterBoundaryFlagSet(INVERT_AC_GRAD); - /* Set Matrix Elements - * - * Loop over locally owned rows of matrix A - * i labels NODE POINT from - * bottom left = (0,0) = 0 - * to - * top right = (meshx-1,meshz-1) = meshx*meshz-1 - * - * i increments by 1 for an increase of 1 in Z - * i increments by meshz for an increase of 1 in X. - * - * In other word the indexing is done in a row-major order, but starting at - * bottom left rather than top left - */ - // X=0 to localmesh->xstart-1 defines the boundary region of the domain. - // Set the values for the inner boundary region - if (localmesh->firstX()) { - for (int x = 0; x < localmesh->xstart; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val; // Value of element to be set in the matrix - // If Neumann Boundary Conditions are set. - if (isInnerBoundaryFlagSet(INVERT_AC_GRAD)) { - // Set values corresponding to nodes adjacent in x - if (fourth_order) { - // Fourth Order Accuracy on Boundary - Element(i, x, z, 0, 0, - -25.0 / (12.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - Element(i, x, z, 1, 0, - 4.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, 2, 0, - -3.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, 3, 0, - 4.0 / (3.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - Element(i, x, z, 4, 0, - -1.0 / (4.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - } else { - // Second Order Accuracy on Boundary - // Element(i,x,z, 0, 0, -3.0 / (2.0*coords->dx(x,y)), MatA ); - // Element(i,x,z, 1, 0, 2.0 / coords->dx(x,y), MatA ); - // Element(i,x,z, 2, 0, -1.0 / (2.0*coords->dx(x,y)), MatA ); - // Element(i,x,z, 3, 0, 0.0, MatA ); // Reset these elements to 0 - // in case 4th order flag was used previously: not allowed now - // Element(i,x,z, 4, 0, 0.0, MatA ); - // Second Order Accuracy on Boundary, set half-way between grid points - Element(i, x, z, 0, 0, - -1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, 1, 0, - 1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, 2, 0, 0.0, MatA); - // Element(i,x,z, 3, 0, 0.0, MatA ); // Reset - // these elements to 0 in case 4th order flag was - // used previously: not allowed now - // Element(i,x,z, 4, 0, 0.0, MatA ); - } - } else { - if (fourth_order) { - // Set Diagonal Values to 1 - Element(i, x, z, 0, 0, 1., MatA); - - // Set off diagonal elements to zero - Element(i, x, z, 1, 0, 0.0, MatA); - Element(i, x, z, 2, 0, 0.0, MatA); - Element(i, x, z, 3, 0, 0.0, MatA); - Element(i, x, z, 4, 0, 0.0, MatA); - } else { - Element(i, x, z, 0, 0, 0.5, MatA); - Element(i, x, z, 1, 0, 0.5, MatA); - Element(i, x, z, 2, 0, 0., MatA); - } - } - - val = 0; // Initialize val - - // Set Components of RHS - // If the inner boundary value should be set by b or x0 - if (isInnerBoundaryFlagSet(INVERT_RHS)) { - val = b[x][z]; - } else if (isInnerBoundaryFlagSet(INVERT_SET)) { - val = x0[x][z]; - } - - // Set components of the RHS (the PETSc vector bs) - // 1 element is being set in row i to val - // INSERT_VALUES replaces existing entries with new values - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - - // Set components of the and trial solution (the PETSc vector xs) - // 1 element is being set in row i to val - // INSERT_VALUES replaces existing entries with new values - val = x0[x][z]; - VecSetValues(xs, 1, &i, &val, INSERT_VALUES); - - i++; // Increment row in Petsc matrix - } - } - } - - // Set the values for the main domain - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - // NOTE: Only A0 is the A from setCoefA () - BoutReal A0, A1, A2, A3, A4, A5; - A0 = A(x, y, z); - - ASSERT3(std::isfinite(A0)); - - // Set the matrix coefficients - Coeffs(x, y, z, A1, A2, A3, A4, A5); - - BoutReal dx = coords->dx(x, y, z); - BoutReal dx2 = SQ(dx); - BoutReal dz = coords->dz(x, y, z); - BoutReal dz2 = SQ(dz); - BoutReal dxdz = dx * dz; - - ASSERT3(std::isfinite(A1)); - ASSERT3(std::isfinite(A2)); - ASSERT3(std::isfinite(A3)); - ASSERT3(std::isfinite(A4)); - ASSERT3(std::isfinite(A5)); - - // Set Matrix Elements - PetscScalar val = 0.; - if (fourth_order) { - // f(i,j) = f(x,z) - val = A0 - (5.0 / 2.0) * ((A1 / dx2) + (A2 / dz2)); - Element(i, x, z, 0, 0, val, MatA); - - // f(i-2,j-2) - val = A3 / (144.0 * dxdz); - Element(i, x, z, -2, -2, val, MatA); - - // f(i-2,j-1) - val = -1.0 * A3 / (18.0 * dxdz); - Element(i, x, z, -2, -1, val, MatA); - - // f(i-2,j) - val = (1.0 / 12.0) * ((-1.0 * A1 / dx2) + (A4 / dx)); - Element(i, x, z, -2, 0, val, MatA); - - // f(i-2,j+1) - val = A3 / (18.0 * dxdz); - Element(i, x, z, -2, 1, val, MatA); - - // f(i-2,j+2) - val = -1.0 * A3 / (144.0 * dxdz); - Element(i, x, z, -2, 2, val, MatA); - - // f(i-1,j-2) - val = -1.0 * A3 / (18.0 * dxdz); - Element(i, x, z, -1, -2, val, MatA); - - // f(i-1,j-1) - val = 4.0 * A3 / (9.0 * dxdz); - Element(i, x, z, -1, -1, val, MatA); - - // f(i-1,j) - val = (4.0 * A1 / (3.0 * dx2)) - (2.0 * A4 / (3.0 * dx)); - Element(i, x, z, -1, 0, val, MatA); - - // f(i-1,j+1) - val = -4.0 * A3 / (9.0 * dxdz); - Element(i, x, z, -1, 1, val, MatA); - - // f(i-1,j+2) - val = A3 / (18.0 * dxdz); - Element(i, x, z, -1, 2, val, MatA); - - // f(i,j-2) - val = (1.0 / 12.0) * ((-1.0 * A2 / dz2) + (A5 / dz)); - Element(i, x, z, 0, -2, val, MatA); - - // f(i,j-1) - val = (4.0 * A2 / (3.0 * dz2)) - (2.0 * A5 / (3.0 * dz)); - Element(i, x, z, 0, -1, val, MatA); - - // f(i,j+1) - val = (4.0 * A2 / (3.0 * dz2)) + (2.0 * A5 / (3.0 * dz)); - Element(i, x, z, 0, 1, val, MatA); - - // f(i,j+2) - val = (-1.0 / 12.0) * ((A2 / dz2) + (A5 / dz)); - Element(i, x, z, 0, 2, val, MatA); - - // f(i+1,j-2) - val = A3 / (18.0 * dxdz); - Element(i, x, z, 1, -2, val, MatA); - - // f(i+1,j-1) - val = -4.0 * A3 / (9.0 * dxdz); - Element(i, x, z, 1, -1, val, MatA); - - // f(i+1,j) - val = (4.0 * A1 / (3.0 * dx2)) + (2.0 * A4 / (3.0 * dx)); - Element(i, x, z, 1, 0, val, MatA); - - // f(i+1,j+1) - val = 4.0 * A3 / (9.0 * dxdz); - Element(i, x, z, 1, 1, val, MatA); - - // f(i+1,j+2) - val = -1.0 * A3 / (18.0 * dxdz); - Element(i, x, z, 1, 2, val, MatA); - - // f(i+2,j-2) - val = -1.0 * A3 / (144.0 * dxdz); - Element(i, x, z, 2, -2, val, MatA); - - // f(i+2,j-1) - val = A3 / (18.0 * dxdz); - Element(i, x, z, 2, -1, val, MatA); - - // f(i+2,j) - val = (-1.0 / 12.0) * ((A1 / dx2) + (A4 / dx)); - Element(i, x, z, 2, 0, val, MatA); - - // f(i+2,j+1) - val = -1.0 * A3 / (18.0 * dxdz); - Element(i, x, z, 2, 1, val, MatA); - - // f(i+2,j+2) - val = A3 / (144.0 * dxdz); - Element(i, x, z, 2, 2, val, MatA); - } else { - // Second order - - // f(i,j) = f(x,z) - val = A0 - 2.0 * ((A1 / dx2) + (A2 / dz2)); - Element(i, x, z, 0, 0, val, MatA); - - // f(i-1,j-1) - val = A3 / (4.0 * dxdz); - Element(i, x, z, -1, -1, val, MatA); - - // f(i-1,j) - val = (A1 / dx2) - A4 / (2.0 * dx); - Element(i, x, z, -1, 0, val, MatA); - - // f(i-1,j+1) - val = -1.0 * A3 / (4.0 * dxdz); - Element(i, x, z, -1, 1, val, MatA); - - // f(i,j-1) - val = (A2 / dz2) - (A5 / (2.0 * dz)); - Element(i, x, z, 0, -1, val, MatA); - - // f(i,j+1) - val = (A2 / dz2) + (A5 / (2.0 * dz)); - Element(i, x, z, 0, 1, val, MatA); - - // f(i+1,j-1) - val = -1.0 * A3 / (4.0 * dxdz); - Element(i, x, z, 1, -1, val, MatA); - - // f(i+1,j) - val = (A1 / dx2) + (A4 / (2.0 * dx)); - Element(i, x, z, 1, 0, val, MatA); - - // f(i+1,j+1) - val = A3 / (4.0 * dxdz); - Element(i, x, z, 1, 1, val, MatA); - } - // Set Components of RHS Vector - val = b[x][z]; - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - - // Set Components of Trial Solution Vector - val = x0[x][z]; - VecSetValues(xs, 1, &i, &val, INSERT_VALUES); - i++; - } + // Set the operator matrix + if (fourth_order) { + setFourthOrderMatrix(yindex, inner_X_neumann, outer_X_neumann); + } else { + setSecondOrderMatrix(yindex, inner_X_neumann, outer_X_neumann); } - // X=localmesh->xend+1 to localmesh->LocalNx-1 defines the upper boundary region of the domain. - // Set the values for the outer boundary region - if (localmesh->lastX()) { - for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - // Set Diagonal Values to 1 - PetscScalar val = 1; - Element(i, x, z, 0, 0, val, MatA); - - // If Neumann Boundary Conditions are set. - if (isOuterBoundaryFlagSet(INVERT_AC_GRAD)) { - // Set values corresponding to nodes adjacent in x - if (fourth_order) { - // Fourth Order Accuracy on Boundary - Element(i, x, z, 0, 0, - 25.0 / (12.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - Element(i, x, z, -1, 0, - -4.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, -2, 0, - 3.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, -3, 0, - -4.0 / (3.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - Element(i, x, z, -4, 0, - 1.0 / (4.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - } else { - // // Second Order Accuracy on Boundary - // Element(i,x,z, 0, 0, 3.0 / (2.0*coords->dx(x,y)), MatA ); - // Element(i,x,z, -1, 0, -2.0 / coords->dx(x,y), MatA ); - // Element(i,x,z, -2, 0, 1.0 / (2.0*coords->dx(x,y)), MatA ); - // Element(i,x,z, -3, 0, 0.0, MatA ); // Reset these elements to 0 - // in case 4th order flag was used previously: not allowed now - // Element(i,x,z, -4, 0, 0.0, MatA ); - // Second Order Accuracy on Boundary, set half-way between grid - // points - Element(i, x, z, 0, 0, - 1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, -1, 0, - -1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, -2, 0, 0.0, MatA); - // Element(i,x,z, -3, 0, 0.0, MatA ); // Reset these elements to 0 - // in case 4th order flag was used previously: not allowed now - // Element(i,x,z, -4, 0, 0.0, MatA ); - } - } else { - if (fourth_order) { - // Set off diagonal elements to zero - Element(i, x, z, -1, 0, 0.0, MatA); - Element(i, x, z, -2, 0, 0.0, MatA); - Element(i, x, z, -3, 0, 0.0, MatA); - Element(i, x, z, -4, 0, 0.0, MatA); - } else { - Element(i, x, z, 0, 0, 0.5, MatA); - Element(i, x, z, -1, 0, 0.5, MatA); - Element(i, x, z, -2, 0, 0., MatA); - } - } - - // Set Components of RHS - // If the inner boundary value should be set by b or x0 - val = 0; - if (isOuterBoundaryFlagSet(INVERT_RHS)) { - val = b[x][z]; - } else if (isOuterBoundaryFlagSet(INVERT_SET)) { - val = x0[x][z]; - } - - // Set components of the RHS (the PETSc vector bs) - // 1 element is being set in row i to val - // INSERT_VALUES replaces existing entries with new values - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - - // Set components of the and trial solution (the PETSc vector xs) - // 1 element is being set in row i to val - // INSERT_VALUES replaces existing entries with new values - val = x0[x][z]; - VecSetValues(xs, 1, &i, &val, INSERT_VALUES); - - i++; // Increment row in Petsc matrix - } - } - } + operator2D.assemble(); + MatSetBlockSize(*operator2D.get(), 1); - if (i != Iend) { - throw BoutException("Petsc index sanity check failed"); + // Declare KSP Context (abstract PETSc object that manages all Krylov methods) + if (ksp_initialised) { + KSPDestroy(&ksp); } - - // Assemble Matrix - MatAssemblyBegin(MatA, MAT_FINAL_ASSEMBLY); - MatAssemblyEnd(MatA, MAT_FINAL_ASSEMBLY); - - // // Record which flags were used for this matrix - // lastflag = flags; - - // Assemble RHS Vector - VecAssemblyBegin(bs); - VecAssemblyEnd(bs); - - // Assemble Trial Solution Vector - VecAssemblyBegin(xs); - VecAssemblyEnd(xs); + KSPCreate(comm, &ksp); // Configure Linear Solver #if PETSC_VERSION_GE(3, 5, 0) - KSPSetOperators(ksp, MatA, MatA); + KSPSetOperators(ksp, *operator2D.get(), *operator2D.get()); #else - KSPSetOperators(ksp, MatA, MatA, DIFFERENT_NONZERO_PATTERN); + KSPSetOperators(ksp, *operator2D.get(), *operator2D.get(), DIFFERENT_NONZERO_PATTERN); #endif - PC pc; // The preconditioner option + PC pc = nullptr; // The preconditioner option if (direct) { // If a direct solver has been chosen // Get the preconditioner @@ -798,22 +319,37 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { } else { KSPSetPCSide(ksp, PC_LEFT); // Left preconditioning } - //ierr = PCShellSetApply(pc,laplacePCapply);CHKERRQ(ierr); - //ierr = PCShellSetContext(pc,this);CHKERRQ(ierr); - //ierr = KSPSetPCSide(ksp, PC_RIGHT);CHKERRQ(ierr); } lib.setOptionsFromInputFile(ksp); } } + PetscVector rhs(b, indexer); + PetscVector guess(x0, indexer); + + // Set boundary conditions + if (!isInnerBoundaryFlagSet(INVERT_RHS)) { + BOUT_FOR_SERIAL(index, indexer->getRegionInnerX()) { + rhs(index) = isInnerBoundaryFlagSet(INVERT_SET) ? x0[index] : 0.0; + } + } + if (!isOuterBoundaryFlagSet(INVERT_RHS)) { + BOUT_FOR_SERIAL(index, indexer->getRegionOuterX()) { + rhs(index) = isInnerBoundaryFlagSet(INVERT_SET) ? x0[index] : 0.0; + } + } + + rhs.assemble(); + guess.assemble(); + // Call the actual solver { - Timer timer("petscsolve"); - KSPSolve(ksp, bs, xs); // Call the solver to solve the system + const Timer timer("petscsolve"); + KSPSolve(ksp, *rhs.get(), *guess.get()); } - KSPConvergedReason reason; + KSPConvergedReason reason = KSP_CONVERGED_ITERATING; KSPGetConvergedReason(ksp, &reason); if (reason == -3) { // Too many iterations, might be fixed by taking smaller timestep throw BoutIterationFail("petsc_laplace: too many iterations"); @@ -824,114 +360,14 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { KSPConvergedReasons[reason], static_cast(reason)); } - // Add data to FieldPerp Object - i = Istart; - // Set the inner boundary values - if (localmesh->firstX()) { - for (int x = 0; x < localmesh->xstart; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val = 0; - VecGetValues(xs, 1, &i, &val); - sol[x][z] = val; - i++; // Increment row in Petsc matrix - } - } - } - - // Set the main domain values - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val = 0; - VecGetValues(xs, 1, &i, &val); - sol[x][z] = val; - i++; // Increment row in Petsc matrix - } - } - - // Set the outer boundary values - if (localmesh->lastX()) { - for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val = 0; - VecGetValues(xs, 1, &i, &val); - sol[x][z] = val; - i++; // Increment row in Petsc matrix - } - } - } - - if (i != Iend) { - throw BoutException("Petsc index sanity check 2 failed"); - } - + auto sol = guess.toField(); + sol.setIndex(yindex); checkData(sol); // Return the solution return sol; } -/*! - * Sets the elements of the matrix A, which is used to solve the problem Ax=b. - * - * \param[in] - * i - * The row of the PETSc matrix - * \param[in] x Local x index of the mesh - * \param[in] z Local z index of the mesh - * \param[in] xshift The shift in rows from the index x - * \param[in] zshift The shift in columns from the index z - * \param[in] ele Value of the element - * \param[in] MatA The matrix A used in the inversion - * - * \param[out] MatA The matrix A used in the inversion - */ -void LaplacePetsc::Element(int i, int x, int z, int xshift, int zshift, PetscScalar ele, - Mat& MatA) { - - // Need to convert LOCAL x to GLOBAL x in order to correctly calculate - // PETSC Matrix Index. - int xoffset = Istart / meshz; - if (Istart % meshz != 0) { - throw BoutException("Petsc index sanity check 3 failed"); - } - - // Calculate the row to be set - int row_new = x + xshift; // should never be out of range. - if (!localmesh->firstX()) { - row_new += (xoffset - localmesh->xstart); - } - - // Calculate the column to be set - int col_new = z + zshift; - if (col_new < 0) { - col_new += meshz; - } else if (col_new > meshz - 1) { - col_new -= meshz; - } - - // Convert to global indices - int index = (row_new * meshz) + col_new; - -#if CHECK > 2 - if (!std::isfinite(ele)) { - throw BoutException("Non-finite element at x={:d}, z={:d}, row={:d}, col={:d}\n", x, - z, i, index); - } -#endif - - /* Inserts or adds a block of values into a matrix - * Input: - * MatA - The matrix to set the values in - * 1 - The number of rows to be set - * &i - The global index of the row - * 1 - The number of columns to be set - * &index - The global index of the column - * &ele - The vlaue to be set - * INSERT_VALUES replaces existing entries with new values - */ - MatSetValues(MatA, 1, &i, 1, &index, &ele, INSERT_VALUES); -} - /*! * Set the matrix components of A in Ax=b, solving * D*Laplace_perp(x) + (1/C1)Grad_perp(C2)*Grad_perp(x) + Ax = B @@ -964,19 +400,19 @@ void LaplacePetsc::Element(int i, int x, int z, int xshift, int zshift, PetscSca * \param[out] coef5 Convenient variable used to set matrix * (see manual for details) */ -void LaplacePetsc::Coeffs(int x, int y, int z, BoutReal& coef1, BoutReal& coef2, - BoutReal& coef3, BoutReal& coef4, BoutReal& coef5) { +LaplacePetsc::CoeffsA LaplacePetsc::Coeffs(Ind3D i) { + const auto x = i.x(); - coef1 = coords->g11(x, y, z); // X 2nd derivative coefficient - coef2 = coords->g33(x, y, z); // Z 2nd derivative coefficient - coef3 = 2. * coords->g13(x, y, z); // X-Z mixed derivative coefficient + BoutReal coef1 = coords->g11[i]; // X 2nd derivative coefficient + BoutReal coef2 = coords->g33[i]; // Z 2nd derivative coefficient + BoutReal coef3 = 2. * coords->g13[i]; // X-Z mixed derivative coefficient - coef4 = 0.0; - coef5 = 0.0; + BoutReal coef4 = 0.0; + BoutReal coef5 = 0.0; // If global flag all_terms are set (true by default) if (all_terms) { - coef4 = coords->G1(x, y, z); // X 1st derivative - coef5 = coords->G3(x, y, z); // Z 1st derivative + coef4 = coords->G1[i]; // X 1st derivative + coef5 = coords->G3[i]; // Z 1st derivative ASSERT3(std::isfinite(coef4)); ASSERT3(std::isfinite(coef5)); @@ -985,71 +421,48 @@ void LaplacePetsc::Coeffs(int x, int y, int z, BoutReal& coef1, BoutReal& coef2, if (nonuniform) { // non-uniform mesh correction if ((x != 0) && (x != (localmesh->LocalNx - 1))) { - coef4 -= 0.5 - * ((coords->dx(x + 1, y, z) - coords->dx(x - 1, y, z)) - / SQ(coords->dx(x, y, z))) + coef4 -= 0.5 * ((coords->dx[i.xp()] - coords->dx[i.xm()]) / SQ(coords->dx[i])) * coef1; // BOUT-06 term } } if (localmesh->IncIntShear) { // d2dz2 term - coef2 += coords->g11(x, y, z) * coords->IntShiftTorsion(x, y, z) - * coords->IntShiftTorsion(x, y, z); + coef2 += coords->g11[i] * coords->IntShiftTorsion[i] * coords->IntShiftTorsion[i]; // Mixed derivative coef3 = 0.0; // This cancels out } if (issetD) { - coef1 *= D(x, y, z); - coef2 *= D(x, y, z); - coef3 *= D(x, y, z); - coef4 *= D(x, y, z); - coef5 *= D(x, y, z); + coef1 *= D[i]; + coef2 *= D[i]; + coef3 *= D[i]; + coef4 *= D[i]; + coef5 *= D[i]; } // A second/fourth order derivative term if (issetC) { - // if( (x > 0) && (x < (localmesh->LocalNx-1)) ) //Valid if doing second order derivative, not if fourth: should only be called for xstart<=x<=xend anyway if ((x > 1) && (x < (localmesh->LocalNx - 2))) { - int zp = z + 1; // z plus 1 - if (zp > meshz - 1) { - zp -= meshz; - } - int zm = z - 1; // z minus 1 - if (zm < 0) { - zm += meshz; - } - BoutReal ddx_C; - BoutReal ddz_C; + BoutReal ddx_C = BoutNaN; + BoutReal ddz_C = BoutNaN; if (fourth_order) { - int zpp = z + 2; // z plus 1 plus 1 - if (zpp > meshz - 1) { - zpp -= meshz; - } - int zmm = z - 2; // z minus 1 minus 1 - if (zmm < 0) { - zmm += meshz; - } // Fourth order discretization of C in x - ddx_C = (-C2(x + 2, y, z) + 8. * C2(x + 1, y, z) - 8. * C2(x - 1, y, z) - + C2(x - 2, y, z)) - / (12. * coords->dx(x, y, z) * (C1(x, y, z))); + ddx_C = (-C2[i.xpp()] + (8. * C2[i.xp()]) - (8. * C2[i.xm()]) + C2[i.xmm()]) + / (12. * coords->dx[i] * (C1[i])); // Fourth order discretization of C in z - ddz_C = (-C2(x, y, zpp) + 8. * C2(x, y, zp) - 8. * C2(x, y, zm) + C2(x, y, zmm)) - / (12. * coords->dz(x, y, z) * (C1(x, y, z))); + ddz_C = (-C2[i.zpp()] + (8. * C2[i.zp()]) - (8. * C2[i.zm()]) + C2[i.zmm()]) + / (12. * coords->dz[i] * (C1[i])); } else { // Second order discretization of C in x - ddx_C = (C2(x + 1, y, z) - C2(x - 1, y, z)) - / (2. * coords->dx(x, y, z) * (C1(x, y, z))); + ddx_C = (C2[i.xp()] - C2[i.xm()]) / (2. * coords->dx[i] * (C1[i])); // Second order discretization of C in z - ddz_C = - (C2(x, y, zp) - C2(x, y, zm)) / (2. * coords->dz(x, y, z) * (C1(x, y, z))); + ddz_C = (C2[i.zp()] - C2[i.zm()]) / (2. * coords->dz[i] * (C1[i])); } - coef4 += coords->g11(x, y, z) * ddx_C + coords->g13(x, y, z) * ddz_C; - coef5 += coords->g13(x, y, z) * ddx_C + coords->g33(x, y, z) * ddz_C; + coef4 += (coords->g11[i] * ddx_C) + (coords->g13[i] * ddz_C); + coef5 += (coords->g13[i] * ddx_C) + (coords->g33[i] * ddz_C); } } @@ -1063,99 +476,199 @@ void LaplacePetsc::Coeffs(int x, int y, int z, BoutReal& coef1, BoutReal& coef2, */ if (issetE) { // These coefficients are 0 by default - coef4 += Ex(x, y, z); - coef5 += Ez(x, y, z); + coef4 += Ex[i]; + coef5 += Ez[i]; } -} - -void LaplacePetsc::vecToField(Vec xs, FieldPerp& f) { - ASSERT1(localmesh == f.getMesh()); + return {coef1, coef2, coef3, coef4, coef5}; +} - f.allocate(); - int i = Istart; - if (localmesh->firstX()) { - for (int x = 0; x < localmesh->xstart; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val; - VecGetValues(xs, 1, &i, &val); - f[x][z] = val; - i++; // Increment row in Petsc matrix - } +void LaplacePetsc::setSecondOrderMatrix(int y, bool inner_X_neumann, + bool outer_X_neumann) { + // Set the boundaries + if (inner_X_neumann) { + const auto dx = sliceXZ(coords->dx, y); + const auto g11 = sliceXZ(coords->g11, y); + + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + const auto factor = 1. / dx[i] / std::sqrt(g11[i]); + operator2D(i, i) = -factor; + operator2D(i, i.xp()) = factor; } - } - - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val; - VecGetValues(xs, 1, &i, &val); - f[x][z] = val; - i++; // Increment row in Petsc matrix + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + operator2D(i, i) = 0.5; + operator2D(i, i.xp()) = 0.5; } } + if (outer_X_neumann) { + const auto dx = sliceXZ(coords->dx, y); + const auto g11 = sliceXZ(coords->g11, y); - if (localmesh->lastX()) { - for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val; - VecGetValues(xs, 1, &i, &val); - f[x][z] = val; - i++; // Increment row in Petsc matrix - } + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + const auto factor = 1. / dx[i] / std::sqrt(g11[i]); + operator2D(i, i) = factor; + operator2D(i, i.xm()) = -factor; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + operator2D(i, i) = 0.5; + operator2D(i, i.xm()) = 0.5; } } - ASSERT1(i == Iend); -} -void LaplacePetsc::fieldToVec(const FieldPerp& f, Vec bs) { - ASSERT1(localmesh == f.getMesh()); + // Set the interior region + BOUT_FOR_SERIAL(l, indexer->getRegionNobndry()) { + const auto i = localmesh->indPerpto3D(l, y); - int i = Istart; - if (localmesh->firstX()) { - for (int x = 0; x < localmesh->xstart; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val = f[x][z]; - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - i++; // Increment row in Petsc matrix - } - } + // NOTE: Only A0 is the A from setCoefA () + const BoutReal A0 = A[i]; + + ASSERT3(std::isfinite(A0)); + + // Set the matrix coefficients + const auto [A1, A2, A3, A4, A5] = Coeffs(i); + + ASSERT3(std::isfinite(A1)); + ASSERT3(std::isfinite(A2)); + ASSERT3(std::isfinite(A3)); + ASSERT3(std::isfinite(A4)); + ASSERT3(std::isfinite(A5)); + + const BoutReal dx = coords->dx[i]; + const BoutReal dx2 = SQ(dx); + const BoutReal dz = coords->dz[i]; + const BoutReal dz2 = SQ(dz); + const BoutReal dxdz = dx * dz; + operator2D(l, l) = A0 - (2.0 * ((A1 / dx2) + (A2 / dz2))); + operator2D(l, l.xm().zm()) = A3 / (4.0 * dxdz); + operator2D(l, l.xm()) = (A1 / dx2) - (A4 / (2.0 * dx)); + operator2D(l, l.xm().zp()) = -1.0 * A3 / (4.0 * dxdz); + operator2D(l, l.zm()) = (A2 / dz2) - (A5 / (2.0 * dz)); + operator2D(l, l.zp()) = (A2 / dz2) + (A5 / (2.0 * dz)); + operator2D(l, l.xp().zm()) = -1.0 * A3 / (4.0 * dxdz); + operator2D(l, l.xp()) = (A1 / dx2) + (A4 / (2.0 * dx)); + operator2D(l, l.xp().zp()) = A3 / (4.0 * dxdz); } +} - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val = f[x][z]; - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - i++; // Increment row in Petsc matrix +void LaplacePetsc::setFourthOrderMatrix(int y, bool inner_X_neumann, + bool outer_X_neumann) { + + // Set boundaries + if (inner_X_neumann) { + const auto dx = sliceXZ(coords->dx, y); + const auto g11 = sliceXZ(coords->g11, y); + + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + const auto factor = 1. / dx[i] / std::sqrt(g11[i]); + operator2D(i, i) = (-25.0 / 12.0) * factor; + operator2D(i, i.xp(1)) = 4.0 * factor; + operator2D(i, i.xp(2)) = -3.0 * factor; + operator2D(i, i.xp(3)) = (4.0 / 3.0) * factor; + operator2D(i, i.xp(4)) = (-1.0 / 4.0) * factor; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + operator2D(i, i) = 1.0; + operator2D(i, i.xp(1)) = 0.0; + operator2D(i, i.xp(2)) = 0.0; + operator2D(i, i.xp(3)) = 0.0; + operator2D(i, i.xp(4)) = 0.0; } } - if (localmesh->lastX()) { - for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { - PetscScalar val = f[x][z]; - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - i++; // Increment row in Petsc matrix - } + if (outer_X_neumann) { + const auto dx = sliceXZ(coords->dx, y); + const auto g11 = sliceXZ(coords->g11, y); + + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + const auto factor = 1. / dx[i] / std::sqrt(g11[i]); + operator2D(i, i) = (25.0 / 12.0) * factor; + operator2D(i, i.xm(1)) = -4.0 * factor; + operator2D(i, i.xm(2)) = 3.0 * factor; + operator2D(i, i.xm(3)) = (-4.0 / 3.0) * factor; + operator2D(i, i.xm(4)) = (1.0 / 4.0) * factor; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + operator2D(i, i) = 1.0; + operator2D(i, i.xm(1)) = 0.0; + operator2D(i, i.xm(2)) = 0.0; + operator2D(i, i.xm(3)) = 0.0; + operator2D(i, i.xm(4)) = 0.0; } } - ASSERT1(i == Iend); - VecAssemblyBegin(bs); - VecAssemblyEnd(bs); + // Set interior region + BOUT_FOR_SERIAL(l, indexer->getRegionNobndry()) { + const auto i = localmesh->indPerpto3D(l, y); + + // NOTE: Only A0 is the A from setCoefA () + const BoutReal A0 = A[i]; + + ASSERT3(std::isfinite(A0)); + + // Set the matrix coefficients + const auto [A1, A2, A3, A4, A5] = Coeffs(i); + + ASSERT3(std::isfinite(A1)); + ASSERT3(std::isfinite(A2)); + ASSERT3(std::isfinite(A3)); + ASSERT3(std::isfinite(A4)); + ASSERT3(std::isfinite(A5)); + + const BoutReal dx = coords->dx[i]; + const BoutReal dx2 = SQ(dx); + const BoutReal dz = coords->dz[i]; + const BoutReal dz2 = SQ(dz); + const BoutReal dxdz = dx * dz; + + operator2D(l, l) = A0 - ((5.0 / 2.0) * ((A1 / dx2) + (A2 / dz2))); + operator2D(l, l.xmm().zmm()) = A3 / (144.0 * dxdz); + operator2D(l, l.xmm().zm()) = -1.0 * A3 / (18.0 * dxdz); + operator2D(l, l.xmm()) = (1.0 / 12.0) * ((-1.0 * A1 / dx2) + (A4 / dx)); + operator2D(l, l.xmm().zp()) = A3 / (18.0 * dxdz); + operator2D(l, l.xmm().zpp()) = -1.0 * A3 / (144.0 * dxdz); + operator2D(l, l.xm().zmm()) = -1.0 * A3 / (18.0 * dxdz); + operator2D(l, l.xm().zm()) = 4.0 * A3 / (9.0 * dxdz); + operator2D(l, l.xm()) = (4.0 * A1 / (3.0 * dx2)) - (2.0 * A4 / (3.0 * dx)); + operator2D(l, l.xm().zp()) = -4.0 * A3 / (9.0 * dxdz); + operator2D(l, l.xm().zpp()) = A3 / (18.0 * dxdz); + operator2D(l, l.zmm()) = (1.0 / 12.0) * ((-1.0 * A2 / dz2) + (A5 / dz)); + operator2D(l, l.zm()) = (4.0 * A2 / (3.0 * dz2)) - (2.0 * A5 / (3.0 * dz)); + operator2D(l, l.zp()) = (4.0 * A2 / (3.0 * dz2)) + (2.0 * A5 / (3.0 * dz)); + operator2D(l, l.zpp()) = (-1.0 / 12.0) * ((A2 / dz2) + (A5 / dz)); + operator2D(l, l.xp().zmm()) = A3 / (18.0 * dxdz); + operator2D(l, l.xp().zm()) = -4.0 * A3 / (9.0 * dxdz); + operator2D(l, l.xp()) = (4.0 * A1 / (3.0 * dx2)) + (2.0 * A4 / (3.0 * dx)); + operator2D(l, l.xp().zp()) = 4.0 * A3 / (9.0 * dxdz); + operator2D(l, l.xp().zpp()) = -1.0 * A3 / (18.0 * dxdz); + operator2D(l, l.xpp().zmm()) = -1.0 * A3 / (144.0 * dxdz); + operator2D(l, l.xpp().zm()) = A3 / (18.0 * dxdz); + operator2D(l, l.xpp()) = (-1.0 / 12.0) * ((A1 / dx2) + (A4 / dx)); + operator2D(l, l.xpp().zp()) = -1.0 * A3 / (18.0 * dxdz); + operator2D(l, l.xpp().zpp()) = A3 / (144.0 * dxdz); + } } /// Preconditioner function int LaplacePetsc::precon(Vec x, Vec y) { - // Get field to be preconditioned - FieldPerp xfield; - vecToField(x, xfield); - xfield.setIndex(sol.getIndex()); // y index stored in sol variable + FieldPerp xfield(indexer->getMesh(), location, yindex); + xfield = 0.0; + + BOUT_FOR_SERIAL(i, indexer->getRegionAll()) { + const auto ind = indexer->getGlobal(i); + PetscScalar val = BoutNaN; + VecGetValues(x, 1, &ind, &val); + xfield[i] = val; + } // Call the preconditioner solver - FieldPerp yfield = pcsolve->solve(xfield); + const FieldPerp yfield = pcsolve->solve(xfield); + + VecCopy(*PetscVector{yfield, indexer}.get(), y); - // Put result into y - fieldToVec(yfield, y); return 0; } diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.hxx b/src/invert/laplace/impls/petsc/petsc_laplace.hxx index b3ee82b8a9..cfd47702c9 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.hxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.hxx @@ -42,11 +42,15 @@ RegisterUnavailableLaplace registerlaplacepetsc(LAPLACE_PETSC, #else +#include #include +#include #include #include #include +#include #include +#include #include @@ -60,12 +64,7 @@ class LaplacePetsc : public Laplacian { public: LaplacePetsc(Options* opt = nullptr, const CELL_LOC loc = CELL_CENTRE, Mesh* mesh_in = nullptr, Solver* solver = nullptr); - ~LaplacePetsc() { - KSPDestroy(&ksp); - VecDestroy(&xs); - VecDestroy(&bs); - MatDestroy(&MatA); - } + ~LaplacePetsc() override; using Laplacian::setCoefA; using Laplacian::setCoefC; @@ -200,9 +199,21 @@ public: int precon(Vec x, Vec y); ///< Preconditioner function private: - void Element(int i, int x, int z, int xshift, int zshift, PetscScalar ele, Mat& MatA); - void Coeffs(int x, int y, int z, BoutReal& A1, BoutReal& A2, BoutReal& A3, BoutReal& A4, - BoutReal& A5); + struct CoeffsA { + BoutReal A1; + BoutReal A2; + BoutReal A3; + BoutReal A4; + BoutReal A5; + }; + + /// Calculate the coefficients ``A1-5`` + CoeffsA Coeffs(Ind3D i); + + /// Set `operator2D` for the second order scheme + void setSecondOrderMatrix(int y, bool inner_X_neumann, bool outer_X_neumann); + /// Set `operator2D` for the fourth order scheme + void setFourthOrderMatrix(int y, bool inner_X_neumann, bool outer_X_neumann); /* Ex and Ez * Additional 1st derivative terms to allow for solution field to be @@ -219,19 +230,14 @@ private: bool issetC; bool issetE; - FieldPerp sol; // solution Field + /// Y-index of solution field. + int yindex = -1; - // Istart is the first row of MatA owned by the process, Iend is 1 greater than the last row. - int Istart, Iend; - - int meshx, meshz, size, - localN; // Mesh sizes, total size, no of points on this processor MPI_Comm comm; - Mat MatA; - Vec xs, bs; // Solution and RHS vectors - KSP ksp; + KSP ksp = nullptr; ///< PETSc solver + bool ksp_initialised = false; - Options* opts; // Laplace Section Options Object + Options* opts; ///< Laplace Section Options Object std::string ksptype; ///< KSP solver type std::string pctype; ///< Preconditioner type @@ -247,14 +253,13 @@ private: bool direct; //Use direct LU solver if true. bool fourth_order; + IndexerPtr indexer; + PetscMatrix operator2D; PetscLib lib; bool rightprec; // Right preconditioning std::unique_ptr pcsolve; // Laplacian solver for preconditioning - void vecToField(Vec x, FieldPerp& f); // Copy a vector into a fieldperp - void fieldToVec(const FieldPerp& f, Vec x); // Copy a fieldperp into a vector - static constexpr int implemented_flags = INVERT_START_NEW; static constexpr int implemented_boundary_flags = INVERT_AC_GRAD | INVERT_SET | INVERT_RHS; diff --git a/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx b/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx index efca1d70be..9966ad654d 100644 --- a/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx +++ b/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx @@ -4,9 +4,9 @@ * Using PETSc Solvers * ************************************************************************** - * Copyright 2013 J. Buchanan, J.Omotani + * Copyright 2013 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -120,39 +120,64 @@ LaplacePetsc3dAmg::LaplacePetsc3dAmg(Options* opt, const CELL_LOC loc, Mesh* mes // Set up boundary conditions in operator const bool inner_X_neumann = isInnerBoundaryFlagSet(INVERT_AC_GRAD); - const auto inner_X_BC = inner_X_neumann ? -1. / coords->dx / sqrt(coords->g_11) : 0.5; - const auto inner_X_BC_plus = inner_X_neumann ? -inner_X_BC : 0.5; - - BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { - operator3D(i, i) = inner_X_BC[i]; - operator3D(i, i.xp()) = inner_X_BC_plus[i]; + if (inner_X_neumann) { + // This is a BinaryExpr that is only evaluated when needed + const auto inner_X_BC = -1. / coords->dx / sqrt(coords->g_11); + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + const BoutReal bc = inner_X_BC[i]; + operator3D(i, i) = bc; + operator3D(i, i.xp()) = -bc; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + operator3D(i, i) = 0.5; + operator3D(i, i.xp()) = 0.5; + } } const bool outer_X_neumann = isOuterBoundaryFlagSet(INVERT_AC_GRAD); - const auto outer_X_BC = outer_X_neumann ? 1. / coords->dx / sqrt(coords->g_11) : 0.5; - const auto outer_X_BC_minus = outer_X_neumann ? -outer_X_BC : 0.5; - - BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { - operator3D(i, i) = outer_X_BC[i]; - operator3D(i, i.xm()) = outer_X_BC_minus[i]; + if (outer_X_neumann) { + const auto outer_X_BC = 1. / coords->dx / sqrt(coords->g_11); + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + const BoutReal bc = outer_X_BC[i]; + operator3D(i, i) = bc; + operator3D(i, i.xm()) = -bc; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + operator3D(i, i) = 0.5; + operator3D(i, i.xm()) = 0.5; + } } const bool lower_Y_neumann = flagSet(lower_boundary_flags, INVERT_AC_GRAD); - const auto lower_Y_BC = lower_Y_neumann ? -1. / coords->dy / sqrt(coords->g_22) : 0.5; - const auto lower_Y_BC_plus = lower_Y_neumann ? -lower_Y_BC : 0.5; - - BOUT_FOR_SERIAL(i, indexer->getRegionLowerY()) { - operator3D(i, i) = lower_Y_BC[i]; - operator3D(i, i.yp()) = lower_Y_BC_plus[i]; + if (lower_Y_neumann) { + const auto lower_Y_BC = -1. / coords->dy / sqrt(coords->g_22); + BOUT_FOR_SERIAL(i, indexer->getRegionLowerY()) { + const BoutReal bc = lower_Y_BC[i]; + operator3D(i, i) = bc; + operator3D(i, i.yp()) = -bc; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionLowerY()) { + operator3D(i, i) = 0.5; + operator3D(i, i.yp()) = 0.5; + } } const bool upper_Y_neumann = flagSet(upper_boundary_flags, INVERT_AC_GRAD); - const auto upper_Y_BC = upper_Y_neumann ? 1. / coords->dy / sqrt(coords->g_22) : 0.5; - const auto upper_Y_BC_minus = upper_Y_neumann ? -upper_Y_BC : 0.5; - - BOUT_FOR_SERIAL(i, indexer->getRegionUpperY()) { - operator3D(i, i) = upper_Y_BC[i]; - operator3D(i, i.ym()) = upper_Y_BC_minus[i]; + if (upper_Y_neumann) { + const auto upper_Y_BC = 1. / coords->dy / sqrt(coords->g_22); + BOUT_FOR_SERIAL(i, indexer->getRegionUpperY()) { + const BoutReal bc = upper_Y_BC[i]; + operator3D(i, i) = bc; + operator3D(i, i.ym()) = -bc; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionUpperY()) { + operator3D(i, i) = 0.5; + operator3D(i, i.ym()) = 0.5; + } } } @@ -174,7 +199,6 @@ void setBC(PetscVector& rhs, const Field3D& b_in, } Field3D LaplacePetsc3dAmg::solve(const Field3D& b_in, const Field3D& x0) { - AUTO_TRACE(); // Timing reported in the log files. Includes any matrix construction. // The timing for just the solve phase can be retrieved from the "petscsolve" @@ -275,7 +299,7 @@ void LaplacePetsc3dAmg::updateMatrix3D() { const Field3D dc_dx = issetC ? DDX(C2) : Field3D(); const Field3D dc_dy = issetC ? DDY(C2) : Field3D(); const Field3D dc_dz = issetC ? DDZ(C2) : Field3D(); - const auto dJ_dy = DDY(coords->J / coords->g_22); + const auto dJ_dy = DDY(Coordinates::FieldMetric{coords->J / coords->g_22}); // Set up the matrix for the internal points on the grid. // Boundary conditions were set in the constructor. @@ -360,7 +384,7 @@ void LaplacePetsc3dAmg::updateMatrix3D() { // Must add these (rather than assign) so that elements used in // interpolation don't overwrite each other. BOUT_FOR_SERIAL(l, indexer->getRegionNobndry()) { - BoutReal C_df_dy = (coords->G2[l] - dJ_dy[l] / coords->J[l]); + BoutReal C_df_dy = coords->G2[l] - (dJ_dy[l] / coords->J[l]); if (issetD) { C_df_dy *= D[l]; } @@ -371,7 +395,7 @@ void LaplacePetsc3dAmg::updateMatrix3D() { / C1[l]; } - BoutReal C_d2f_dy2 = (coords->g22[l] - 1.0 / coords->g_22[l]); + BoutReal C_d2f_dy2 = coords->g22[l] - (1.0 / coords->g_22[l]); if (issetD) { C_d2f_dy2 *= D[l]; } diff --git a/src/invert/laplace/impls/serial_band/serial_band.cxx b/src/invert/laplace/impls/serial_band/serial_band.cxx index d7b4ac5d3b..0cf8d7259d 100644 --- a/src/invert/laplace/impls/serial_band/serial_band.cxx +++ b/src/invert/laplace/impls/serial_band/serial_band.cxx @@ -45,6 +45,9 @@ LaplaceSerialBand::LaplaceSerialBand(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), Acoef(0.0), Ccoef(1.0), Dcoef(1.0) { + + bout::fft::assertZSerial(*localmesh, "`band` inversion"); + Acoef.setLocation(location); Ccoef.setLocation(location); Dcoef.setLocation(location); diff --git a/src/invert/laplace/impls/serial_tri/serial_tri.cxx b/src/invert/laplace/impls/serial_tri/serial_tri.cxx index 0fb9294d76..d051ce0c1e 100644 --- a/src/invert/laplace/impls/serial_tri/serial_tri.cxx +++ b/src/invert/laplace/impls/serial_tri/serial_tri.cxx @@ -24,6 +24,10 @@ * **************************************************************************/ +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include "serial_tri.hxx" #include "bout/globals.hxx" @@ -33,14 +37,15 @@ #include #include #include -#include -#include - #include +#include LaplaceSerialTri::LaplaceSerialTri(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), A(0.0), C(1.0), D(1.0) { + + bout::fft::assertZSerial(*localmesh, "`tri` inversion"); + A.setLocation(location); C.setLocation(location); D.setLocation(location); @@ -246,3 +251,5 @@ FieldPerp LaplaceSerialTri::solve(const FieldPerp& b, const FieldPerp& x0) { return x; // Result of the inversion } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/serial_tri/serial_tri.hxx b/src/invert/laplace/impls/serial_tri/serial_tri.hxx index 5b0419fa27..4aed777b7c 100644 --- a/src/invert/laplace/impls/serial_tri/serial_tri.hxx +++ b/src/invert/laplace/impls/serial_tri/serial_tri.hxx @@ -29,8 +29,19 @@ class LaplaceSerialTri; #ifndef BOUT_SERIAL_TRI_H #define BOUT_SERIAL_TRI_H +#include "bout/build_defines.hxx" +#include "bout/invert_laplace.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacetri(LAPLACE_TRI, "BOUT++ was configured with 3D metrics"); +} + +#else + #include -#include #include namespace { @@ -80,4 +91,6 @@ private: Field2D A, C, D; }; +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_SERIAL_TRI_H diff --git a/src/invert/laplace/impls/spt/spt.cxx b/src/invert/laplace/impls/spt/spt.cxx index 2e4c844c94..34a1a64cae 100644 --- a/src/invert/laplace/impls/spt/spt.cxx +++ b/src/invert/laplace/impls/spt/spt.cxx @@ -31,6 +31,10 @@ * */ +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include #include #include @@ -45,6 +49,9 @@ LaplaceSPT::LaplaceSPT(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), Acoef(0.0), Ccoef(1.0), Dcoef(1.0) { + + bout::fft::assertZSerial(*localmesh, "`spt` inversion"); + Acoef.setLocation(location); Ccoef.setLocation(location); Dcoef.setLocation(location); @@ -95,7 +102,7 @@ FieldPerp LaplaceSPT::solve(const FieldPerp& b, const FieldPerp& x0) { if (isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) { // Copy x0 inner boundary into bs for (int ix = 0; ix < xbndry; ix++) { - for (int iz = 0; iz < localmesh->LocalNz; iz++) { + for (int iz = localmesh->zstart; iz <= localmesh->zend; iz++) { bs[ix][iz] = x0[ix][iz]; } } @@ -103,7 +110,7 @@ FieldPerp LaplaceSPT::solve(const FieldPerp& b, const FieldPerp& x0) { if (isOuterBoundaryFlagSetOnLastX(INVERT_SET)) { // Copy x0 outer boundary into bs for (int ix = localmesh->LocalNx - 1; ix >= localmesh->LocalNx - xbndry; ix--) { - for (int iz = 0; iz < localmesh->LocalNz; iz++) { + for (int iz = localmesh->zstart; iz <= localmesh->zend; iz++) { bs[ix][iz] = x0[ix][iz]; } } @@ -181,7 +188,7 @@ Field3D LaplaceSPT::solve(const Field3D& b, const Field3D& x0) { // Copy x0 inner boundary into bs for (int ix = 0; ix < xbndry; ix++) { for (int iy = 0; iy < localmesh->LocalNy; iy++) { - for (int iz = 0; iz < localmesh->LocalNz; iz++) { + for (int iz = localmesh->zstart; iz <= localmesh->zend; iz++) { bs(ix, iy, iz) = x0(ix, iy, iz); } } @@ -191,7 +198,7 @@ Field3D LaplaceSPT::solve(const Field3D& b, const Field3D& x0) { // Copy x0 outer boundary into bs for (int ix = localmesh->LocalNx - 1; ix >= localmesh->LocalNx - xbndry; ix--) { for (int iy = 0; iy < localmesh->LocalNy; iy++) { - for (int iz = 0; iz < localmesh->LocalNz; iz++) { + for (int iz = localmesh->zstart; iz <= localmesh->zend; iz++) { bs(ix, iy, iz) = x0(ix, iy, iz); } } @@ -341,14 +348,14 @@ int LaplaceSPT::start(const FieldPerp& b, SPT_data& data) { // Send data localmesh->sendXOut(std::begin(data.buffer), 4 * (maxmode + 1), data.comm_tag); - } else if (localmesh->PE_XIND == 1) { + } else if (localmesh->getXProcIndex() == 1) { // Post a receive data.recv_handle = localmesh->irecvXIn(std::begin(data.buffer), 4 * (maxmode + 1), data.comm_tag); } data.proc++; // Now moved onto the next processor - if (localmesh->NXPE == 2) { + if (localmesh->getNXPE() == 2) { data.dir = -1; // Special case. Otherwise reversal handled in spt_continue } @@ -366,7 +373,7 @@ int LaplaceSPT::next(SPT_data& data) { return 1; } - if (localmesh->PE_XIND == data.proc) { + if (localmesh->getXProcIndex() == data.proc) { /// This processor's turn to do inversion // Wait for data to arrive @@ -450,7 +457,7 @@ int LaplaceSPT::next(SPT_data& data) { } } - if (localmesh->PE_XIND != 0) { // If not finished yet + if (localmesh->getXProcIndex() != 0) { // If not finished yet /// Send data if (data.dir > 0) { @@ -460,7 +467,7 @@ int LaplaceSPT::next(SPT_data& data) { } } - } else if (localmesh->PE_XIND == data.proc + data.dir) { + } else if (localmesh->getXProcIndex() == data.proc + data.dir) { // This processor is next, post receive if (data.dir > 0) { @@ -474,7 +481,7 @@ int LaplaceSPT::next(SPT_data& data) { data.proc += data.dir; - if (data.proc == localmesh->NXPE - 1) { + if (data.proc == localmesh->getNXPE() - 1) { data.dir = -1; // Reverses direction at the end } @@ -519,7 +526,7 @@ void LaplaceSPT::finish(SPT_data& data, FieldPerp& x) { if (!localmesh->firstX()) { // Set left boundary to zero (Prevent unassigned values in corners) for (int ix = 0; ix < localmesh->xstart; ix++) { - for (int kz = 0; kz < localmesh->LocalNz; kz++) { + for (int kz = localmesh->zstart; kz <= localmesh->zend; kz++) { x(ix, kz) = 0.0; } } @@ -527,7 +534,7 @@ void LaplaceSPT::finish(SPT_data& data, FieldPerp& x) { if (!localmesh->lastX()) { // Same for right boundary for (int ix = localmesh->xend + 1; ix < localmesh->LocalNx; ix++) { - for (int kz = 0; kz < localmesh->LocalNz; kz++) { + for (int kz = localmesh->zstart; kz <= localmesh->zend; kz++) { x(ix, kz) = 0.0; } } @@ -550,3 +557,5 @@ void LaplaceSPT::SPT_data::allocate(int mm, int nx) { buffer.reallocate(4 * mm); } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/spt/spt.hxx b/src/invert/laplace/impls/spt/spt.hxx index a9d5b2583f..a3b1acb7c3 100644 --- a/src/invert/laplace/impls/spt/spt.hxx +++ b/src/invert/laplace/impls/spt/spt.hxx @@ -39,10 +39,20 @@ class LaplaceSPT; #ifndef BOUT_SPT_H +#include "bout/build_defines.hxx" +#include "bout/invert_laplace.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacespt(LAPLACE_SPT, "BOUT++ was configured with 3D metrics"); +} + +#else #define BOUT_SPT_H #include -#include #include #include #include @@ -153,7 +163,9 @@ private: namespace { // Note: After class definition so compiler knows that // registered class is derived from Laplacian -RegisterLaplace registerlaplacespt(LAPLACE_SPT); +const RegisterLaplace registerlaplacespt(LAPLACE_SPT); } // namespace +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_SPT_H diff --git a/src/invert/laplace/invert_laplace.cxx b/src/invert/laplace/invert_laplace.cxx index 4032499781..9af6fd20f9 100644 --- a/src/invert/laplace/invert_laplace.cxx +++ b/src/invert/laplace/invert_laplace.cxx @@ -33,17 +33,18 @@ #include #include +#include #include #include #include #include -#include #include #include #include #include #include #include +#include #include // Implementations: @@ -90,19 +91,12 @@ Laplacian::Laplacian(Options* options, const CELL_LOC loc, Mesh* mesh_in, const BoutReal filter = (*options)["filter"] .doc("Fraction of Z modes to filter out. Between 0 and 1") .withDefault(0.0); - const int ncz = localmesh->LocalNz; + const int ncz = localmesh->zend - localmesh->zstart + 1; // convert filtering into an integer number of modes maxmode = (*options)["maxmode"] .doc("The maximum Z mode to solve for") - .withDefault(ROUND((1.0 - filter) * static_cast(ncz / 2))); - - // Clamp maxmode between 0 and nz/2 - if (maxmode < 0) { - maxmode = 0; - } - if (maxmode > ncz / 2) { - maxmode = ncz / 2; - } + .withDefault(ROUND((1.0 - filter) * static_cast(ncz) / 2.0)); + maxmode = std::clamp(maxmode, 0, ncz / 2); low_mem = (*options)["low_mem"] .doc("If true, reduce the amount of memory used") @@ -155,7 +149,6 @@ void Laplacian::cleanup() { instance.reset(); } **********************************************************************************/ Field3D Laplacian::solve(const Field3D& b) { - TRACE("Laplacian::solve(Field3D)"); ASSERT1(b.getLocation() == location); ASSERT1(localmesh == b.getMesh()); @@ -216,7 +209,6 @@ Field2D Laplacian::solve(const Field2D& b) { * \returns x All the y-slices of x_slice in the equation A*x_slice = b_slice */ Field3D Laplacian::solve(const Field3D& b, const Field3D& x0) { - TRACE("Laplacian::solve(Field3D, Field3D)"); ASSERT1(b.getLocation() == location); ASSERT1(x0.getLocation() == location); @@ -226,10 +218,10 @@ Field3D Laplacian::solve(const Field3D& b, const Field3D& x0) { // Setting the start and end range of the y-slices int ys = localmesh->ystart, ye = localmesh->yend; - if (localmesh->hasBndryLowerY() && include_yguards) { + if (include_yguards && localmesh->hasBndryLowerY()) { ys = 0; // Mesh contains a lower boundary } - if (localmesh->hasBndryUpperY() && include_yguards) { + if (include_yguards && localmesh->hasBndryUpperY()) { ye = localmesh->LocalNy - 1; // Contains upper boundary } @@ -261,7 +253,7 @@ Field2D Laplacian::solve(const Field2D& b, const Field2D& x0) { **********************************************************************************/ void Laplacian::tridagCoefs(int jx, int jy, int jz, dcomplex& a, dcomplex& b, dcomplex& c, - const Field2D* ccoef, const Field2D* d, CELL_LOC loc) { + const Field2D* ccoef, const Field2D* d, CELL_LOC loc) const { if (loc == CELL_DEFAULT) { loc = location; @@ -278,13 +270,13 @@ void Laplacian::tridagCoefs(int jx, int jy, int jz, dcomplex& a, dcomplex& b, dc void Laplacian::tridagCoefs(int /* jx */, int /* jy */, BoutReal /* kwave */, dcomplex& /* a */, dcomplex& /* b */, dcomplex& /* c */, const Field2D* /* c1coef */, const Field2D* /* c2coef */, - const Field2D* /* d */, CELL_LOC /* loc */) { + const Field2D* /* d */, CELL_LOC /* loc */) const { throw BoutException("Laplacian::tridagCoefs() does not support 3d metrics."); } #else void Laplacian::tridagCoefs(int jx, int jy, BoutReal kwave, dcomplex& a, dcomplex& b, dcomplex& c, const Field2D* c1coef, const Field2D* c2coef, - const Field2D* d, CELL_LOC loc) { + const Field2D* d, CELL_LOC loc) const { /* Function: Laplacian::tridagCoef * Purpose: - Set the matrix components of A in Ax=b, solving * @@ -426,14 +418,14 @@ void Laplacian::tridagMatrix(dcomplex* /*avec*/, dcomplex* /*bvec*/, dcomplex* / dcomplex* /*bk*/, int /*jy*/, int /*kz*/, BoutReal /*kwave*/, const Field2D* /*a*/, const Field2D* /*c1coef*/, const Field2D* /*c2coef*/, const Field2D* /*d*/, - bool /*includeguards*/, bool /*zperiodic*/) { + bool /*includeguards*/, bool /*zperiodic*/) const { throw BoutException("Error: tridagMatrix does not yet work with 3D metric."); } #else void Laplacian::tridagMatrix(dcomplex* avec, dcomplex* bvec, dcomplex* cvec, dcomplex* bk, int jy, int kz, BoutReal kwave, const Field2D* a, const Field2D* c1coef, const Field2D* c2coef, - const Field2D* d, bool includeguards, bool zperiodic) { + const Field2D* d, bool includeguards, bool zperiodic) const { ASSERT1(a->getLocation() == location); ASSERT1(c1coef->getLocation() == location); ASSERT1(c2coef->getLocation() == location); @@ -462,7 +454,8 @@ void Laplacian::tridagMatrix(dcomplex* avec, dcomplex* bvec, dcomplex* cvec, dco // Setting the width of the boundary. // NOTE: The default is a width of (localmesh->xstart) guard cells - int inbndry = localmesh->xstart, outbndry = localmesh->xstart; + int inbndry = localmesh->xstart; + int outbndry = localmesh->xstart; // If the flags to assign that only one guard cell should be used is set if (isGlobalFlagSet(INVERT_BOTH_BNDRY_ONE) || (localmesh->xstart < 2)) { diff --git a/src/invert/laplacexy2/laplacexy2_hypre.cxx b/src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx similarity index 95% rename from src/invert/laplacexy2/laplacexy2_hypre.cxx rename to src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx index a5028169f8..439d07a4e5 100644 --- a/src/invert/laplacexy2/laplacexy2_hypre.cxx +++ b/src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx @@ -1,15 +1,26 @@ -#include +#include "bout/build_defines.hxx" -#if BOUT_HAS_HYPRE +#if BOUT_HAS_HYPRE and not BOUT_USE_METRIC_3D + +#include "laplacexy-hypre.hxx" #include "bout/assert.hxx" +#include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" +#include "bout/coordinates.hxx" +#include "bout/field2d.hxx" +#include "bout/globalindexer.hxx" #include "bout/globals.hxx" #include "bout/mesh.hxx" +#include "bout/operatorstencil.hxx" +#include "bout/options.hxx" #include "bout/output.hxx" +#include "bout/region.hxx" +#include "bout/sys/range.hxx" #include "bout/sys/timer.hxx" -#include +#include +#include #if BOUT_HAS_CUDA && defined(__CUDACC__) #define gpuErrchk(ans) \ @@ -226,7 +237,7 @@ void LaplaceXY2Hypre::setCoefs(const Field2D& A, const Field2D& B) { } } -Field2D LaplaceXY2Hypre::solve(Field2D& rhs, Field2D& x0) { +Field2D LaplaceXY2Hypre::solve(const Field2D& rhs, const Field2D& x0) { Timer timer("invert"); ASSERT1(rhs.getMesh() == localmesh); diff --git a/include/bout/invert/laplacexy2_hypre.hxx b/src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx similarity index 71% rename from include/bout/invert/laplacexy2_hypre.hxx rename to src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx index 1df0988d06..f9b1d90fb3 100644 --- a/include/bout/invert/laplacexy2_hypre.hxx +++ b/src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx @@ -33,51 +33,45 @@ #ifndef LAPLACE_XY2_HYPRE_H #define LAPLACE_XY2_HYPRE_H -#include +#include "bout/build_defines.hxx" +#include "bout/invert/laplacexy.hxx" -#if not BOUT_HAS_HYPRE -// If no Hypre +#if !BOUT_HAS_HYPRE -#include "bout/globalindexer.hxx" -#include -#include -#include - -/*! - * Create a dummy class so that code will compile - * without Hypre, but will throw an exception if - * LaplaceXY is used. - */ -class LaplaceXY2Hypre { -public: - LaplaceXY2Hypre(Mesh* UNUSED(m) = nullptr, Options* UNUSED(opt) = nullptr, - const CELL_LOC UNUSED(loc) = CELL_CENTRE) { - throw BoutException("LaplaceXY requires Hypre. No LaplaceXY available"); - } - void setCoefs(const Field2D& UNUSED(A), const Field2D& UNUSED(B)) {} - Field2D solve(const Field2D& UNUSED(rhs), const Field2D& UNUSED(x0)) { - throw BoutException("LaplaceXY requires Hypre. No LaplaceXY available"); - } -}; +namespace { +RegisterUnavailableLaplaceXY + registerlaplacexyhypre("hypre", "BOUT++ was not configured with HYPRE"); +} + +#elif BOUT_USE_METRIC_3D +namespace { +RegisterUnavailableLaplaceXY + registerlaplacexyhypre("hypre", "BOUT++ was configured with 3D metrics"); +} #else // BOUT_HAS_HYPRE class Mesh; -#include "bout/utils.hxx" -#include +#include "bout/bout_types.hxx" +#include "bout/field2d.hxx" +#include "bout/globalindexer.hxx" +#include "bout/hypre_interface.hxx" -class LaplaceXY2Hypre { +class LaplaceXY2Hypre : public LaplaceXY { public: - LaplaceXY2Hypre(Mesh* m = nullptr, Options* opt = nullptr, - const CELL_LOC loc = CELL_CENTRE); - ~LaplaceXY2Hypre() = default; + LaplaceXY2Hypre(Mesh* m = nullptr, Options* opt = nullptr, CELL_LOC loc = CELL_CENTRE); + LaplaceXY2Hypre(const LaplaceXY2Hypre&) = delete; + LaplaceXY2Hypre(LaplaceXY2Hypre&&) = delete; + LaplaceXY2Hypre& operator=(const LaplaceXY2Hypre&) = delete; + LaplaceXY2Hypre& operator=(LaplaceXY2Hypre&&) = delete; + ~LaplaceXY2Hypre() override = default; /*! * Set coefficients (A, B) in equation: * Div( A * Grad_perp(x) ) + B*x = b */ - void setCoefs(const Field2D& A, const Field2D& B); + void setCoefs(const Field2D& A, const Field2D& B) override; /*! * Solve Laplacian in X-Y @@ -96,7 +90,7 @@ public: * The solution as a Field2D. On failure an exception will be raised * */ - Field2D solve(Field2D& rhs, Field2D& x0); + Field2D solve(const Field2D& rhs, const Field2D& x0) override; /*! * Preconditioner function @@ -131,5 +125,9 @@ private: MPI_Comm communicator(); }; +namespace { +const inline RegisterLaplaceXY registerlaplacexyhypre{"hypre"}; +} + #endif // BOUT_HAS_HYPRE #endif // LAPLACE_XY_HYPRE_H diff --git a/src/invert/laplacexy/impls/petsc/laplacexy-petsc.cxx b/src/invert/laplacexy/impls/petsc/laplacexy-petsc.cxx new file mode 100644 index 0000000000..04abecfe05 --- /dev/null +++ b/src/invert/laplacexy/impls/petsc/laplacexy-petsc.cxx @@ -0,0 +1,1899 @@ +#include "bout/build_defines.hxx" + +#if BOUT_HAS_PETSC + +#include "laplacexy-petsc.hxx" + +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutcomm.hxx" +#include "bout/boutexception.hxx" +#include "bout/cyclic_reduction.hxx" +#include "bout/derivs.hxx" +#include "bout/field2d.hxx" +#include "bout/globals.hxx" +#include "bout/options.hxx" +#include "bout/petsclib.hxx" +#include "bout/solver.hxx" +#include "bout/sys/range.hxx" +#include "bout/sys/timer.hxx" +#include "bout/utils.hxx" + +#include +#include + +#include + +namespace { +PetscErrorCode laplacePCapply(PC pc, Vec x, Vec y) { + // Get the context + LaplaceXYpetsc* s = nullptr; + const auto ierr = PCShellGetContext(pc, reinterpret_cast(&s)); + CHKERRQ(ierr); + + PetscFunctionReturn(s->precon(x, y)); +} +} // namespace + +LaplaceXYpetsc::LaplaceXYpetsc(Mesh* m, Options* opt, const CELL_LOC loc) + : lib(opt == nullptr ? &(Options::root()["laplacexy"]) : opt), + localmesh(m == nullptr ? bout::globals::mesh : m), location(loc), monitor(*this) { + Timer timer("invert"); + + if (opt == nullptr) { + // If no options supplied, use default + opt = &(Options::root()["laplacexy"]); + } + + finite_volume = + (*opt)["finite_volume"] + .doc("Use finite volume rather than finite difference discretisation.") + .withDefault(true); + + /////////////////////////////////////////////////// + // Boundary condititions options + if (localmesh->periodicY(localmesh->xstart)) { + // Periodic in Y, so in the core + x_inner_dirichlet = (*opt)["core_bndry_dirichlet"].withDefault(false); + } else { + // Non-periodic, so in the PF region + x_inner_dirichlet = (*opt)["pf_bndry_dirichlet"].withDefault(true); + } + if ((*opt)["y_bndry_dirichlet"].isSet()) { + throw BoutException("y_bndry_dirichlet has been deprecated. Use y_bndry=dirichlet " + "instead."); + } + y_bndry = (*opt)["y_bndry"].withDefault("neumann"); + + // Check value of y_bndry is a supported option + if (not(y_bndry == "dirichlet" or y_bndry == "neumann" or y_bndry == "free_o3")) { + + throw BoutException("Unrecognized option '{}' for laplacexy:ybndry", y_bndry); + } + + if (not finite_volume) { + // Check we can use corner cells + if (not localmesh->include_corner_cells) { + throw BoutException( + "Finite difference form of LaplaceXYpetsc allows non-orthogonal x- and " + "y-directions, so requires mesh:include_corner_cells=true."); + } + } + + // Use name of options section as the default prefix for performance logging variables + default_prefix = opt->name(); + + // Get MPI communicator + auto comm = BoutComm::get(); + + // Local size + const int localN = localSize(); + + // Create Vectors + VecCreate(comm, &xs); + VecSetSizes(xs, localN, PETSC_DETERMINE); + VecSetFromOptions(xs); + VecDuplicate(xs, &bs); + + // Set size of Matrix on each processor to localN x localN + MatCreate(comm, &MatA); + MatSetSizes(MatA, localN, localN, PETSC_DETERMINE, PETSC_DETERMINE); + MatSetFromOptions(MatA); + + ////////////////////////////////////////////////// + // Specify local indices. This creates a mapping + // from local indices to index, using a Field2D object + + indexXY = -1; // Set all points to -1, indicating out of domain + + int ind = 0; + + // Y boundaries + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + indexXY(it.ind, localmesh->ystart - 1) = ind++; + } + if ((not finite_volume) and localmesh->hasBndryLowerY()) { + // Corner boundary cells + if (localmesh->firstX()) { + indexXY(localmesh->xstart - 1, localmesh->ystart - 1) = ind++; + } + if (localmesh->lastX()) { + indexXY(localmesh->xend + 1, localmesh->ystart - 1) = ind++; + } + } + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + indexXY(it.ind, localmesh->yend + 1) = ind++; + } + if ((not finite_volume) and localmesh->hasBndryUpperY()) { + // Corner boundary cells + if (localmesh->firstX()) { + indexXY(localmesh->xstart - 1, localmesh->yend + 1) = ind++; + } + if (localmesh->lastX()) { + indexXY(localmesh->xend + 1, localmesh->yend + 1) = ind++; + } + } + + xstart = localmesh->xstart; + if (localmesh->firstX()) { + xstart -= 1; // Include X guard cells + } + xend = localmesh->xend; + if (localmesh->lastX()) { + xend += 1; + } + for (int x = xstart; x <= xend; x++) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + indexXY(x, y) = ind++; + } + } + + ASSERT1(ind == localN); // Reached end of range + + ////////////////////////////////////////////////// + // Allocate storage for preconditioner + + nloc = xend - xstart + 1; // Number of X points on this processor + nsys = localmesh->yend - localmesh->ystart + 1; // Number of separate Y slices + + acoef.reallocate(nsys, nloc); + bcoef.reallocate(nsys, nloc); + ccoef.reallocate(nsys, nloc); + xvals.reallocate(nsys, nloc); + bvals.reallocate(nsys, nloc); + + // Create a cyclic reduction object + cr = bout::utils::make_unique>(localmesh->getXcomm(), nloc); + + ////////////////////////////////////////////////// + // Pre-allocate PETSc storage + + PetscInt* d_nnz = nullptr; + PetscInt* o_nnz = nullptr; + PetscMalloc((localN) * sizeof(PetscInt), &d_nnz); + PetscMalloc((localN) * sizeof(PetscInt), &o_nnz); + + if (finite_volume) { + setPreallocationFiniteVolume(d_nnz, o_nnz); + } else { + setPreallocationFiniteDifference(d_nnz, o_nnz); + } + // Pre-allocate + MatMPIAIJSetPreallocation(MatA, 0, d_nnz, 0, o_nnz); + MatSetUp(MatA); + + PetscFree(d_nnz); + PetscFree(o_nnz); + + // Determine which row/columns of the matrix are locally owned + int Istart = 0; + int Iend = 0; + MatGetOwnershipRange(MatA, &Istart, &Iend); + + // Convert indexXY from local index to global index + indexXY += Istart; + + // Now communicate to fill guard cells + // Note, this includes corner cells if necessary + localmesh->communicate(indexXY); + + ////////////////////////////////////////////////// + // Set up KSP + + // Declare KSP Context + KSPCreate(comm, &ksp); + + // Configure Linear Solver + + const bool direct = (*opt)["direct"].doc("Use a direct LU solver").withDefault(false); + + if (direct) { + KSPGetPC(ksp, &pc); + PCSetType(pc, PCLU); +#if PETSC_VERSION_GE(3, 9, 0) + PCFactorSetMatSolverType(pc, "mumps"); +#else + PCFactorSetMatSolverPackage(pc, "mumps"); +#endif + } else { + + // Convergence Parameters. Solution is considered converged if |r_k| < max( rtol * |b| , atol ) + // where r_k = b - Ax_k. The solution is considered diverged if |r_k| > dtol * |b|. + + const BoutReal rtol = (*opt)["rtol"].doc("Relative tolerance").withDefault(1e-5); + const BoutReal atol = + (*opt)["atol"] + .doc("Absolute tolerance. The solution is considered converged if |Ax-b| " + "< max( rtol * |b| , atol )") + .withDefault(1e-10); + const BoutReal dtol = + (*opt)["dtol"] + .doc("The solution is considered diverged if |Ax-b| > dtol * |b|") + .withDefault(1e3); + const int maxits = (*opt)["maxits"].doc("Maximum iterations").withDefault(100000); + + // Get KSP Solver Type + const std::string ksptype = + (*opt)["ksptype"].doc("KSP solver type").withDefault("gmres"); + + // Get PC type + const std::string pctype = + (*opt)["pctype"].doc("Preconditioner type").withDefault("none"); + + KSPSetType(ksp, ksptype.c_str()); + KSPSetTolerances(ksp, rtol, atol, dtol, maxits); + + KSPSetInitialGuessNonzero(ksp, static_cast(true)); + + KSPGetPC(ksp, &pc); + PCSetType(pc, pctype.c_str()); + + if (pctype == "shell") { + // Using tridiagonal solver as preconditioner + PCShellSetApply(pc, laplacePCapply); + PCShellSetContext(pc, this); + + const bool rightprec = + (*opt)["rightprec"].doc("Use right preconditioning?").withDefault(true); + if (rightprec) { + KSPSetPCSide(ksp, PC_RIGHT); // Right preconditioning + } else { + KSPSetPCSide(ksp, PC_LEFT); // Left preconditioning + } + } + } + + lib.setOptionsFromInputFile(ksp); + + /////////////////////////////////////////////////// + // Including Y derivatives? + + include_y_derivs = (*opt)["include_y_derivs"] + .doc("Include Y derivatives in operator to invert?") + .withDefault(true); + + /////////////////////////////////////////////////// + // Set the default coefficients + Field2D one(1., localmesh); + Field2D zero(0., localmesh); + one.setLocation(location); + zero.setLocation(location); + setCoefs(one, zero); +} + +void LaplaceXYpetsc::setPreallocationFiniteVolume(PetscInt* d_nnz, PetscInt* o_nnz) { + const int localN = localSize(); + + // This discretisation uses a 5-point stencil + for (int i = 0; i < localN; i++) { + // Non-zero elements on this processor + d_nnz[i] = 5; // Star pattern in 2D + // Non-zero elements on neighboring processor + o_nnz[i] = 0; + } + + // X boundaries + if (localmesh->firstX()) { + // Lower X boundary + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int localIndex = globalIndex(localmesh->xstart - 1, y); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + + d_nnz[localIndex] = 2; // Diagonal sub-matrix + o_nnz[localIndex] = 0; // Off-diagonal sub-matrix + } + } else { + // On another processor + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int localIndex = globalIndex(localmesh->xstart, y); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] -= 1; + o_nnz[localIndex] += 1; + } + } + if (localmesh->lastX()) { + // Upper X boundary + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int localIndex = globalIndex(localmesh->xend + 1, y); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = 2; // Diagonal sub-matrix + o_nnz[localIndex] = 0; // Off-diagonal sub-matrix + } + } else { + // On another processor + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int localIndex = globalIndex(localmesh->xend, y); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] -= 1; + o_nnz[localIndex] += 1; + } + } + // Y boundaries + + for (int x = localmesh->xstart; x <= localmesh->xend; x++) { + // Default to no boundary + // NOTE: This assumes that communications in Y are to other + // processors. If Y is communicated with this processor (e.g. NYPE=1) + // then this will result in PETSc warnings about out of range allocations + { + const int localIndex = globalIndex(x, localmesh->ystart); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + // d_nnz[localIndex] -= 1; // Note: Slightly inefficient + o_nnz[localIndex] += 1; + } + { + const int localIndex = globalIndex(x, localmesh->yend); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + // d_nnz[localIndex] -= 1; // Note: Slightly inefficient + o_nnz[localIndex] += 1; + } + } + + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + { + const int localIndex = globalIndex(it.ind, localmesh->ystart - 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = 2; // Diagonal sub-matrix + o_nnz[localIndex] = 0; // Off-diagonal sub-matrix + } + { + const int localIndex = globalIndex(it.ind, localmesh->ystart); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] += 1; + o_nnz[localIndex] -= 1; + } + } + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + { + const int localIndex = globalIndex(it.ind, localmesh->yend + 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = 2; // Diagonal sub-matrix + o_nnz[localIndex] = 0; // Off-diagonal sub-matrix + } + { + const int localIndex = globalIndex(it.ind, localmesh->yend); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] += 1; + o_nnz[localIndex] -= 1; + } + } +} + +void LaplaceXYpetsc::setPreallocationFiniteDifference(PetscInt* d_nnz, PetscInt* o_nnz) { + const int localN = localSize(); + + // This discretisation uses a 9-point stencil + for (int i = 0; i < localN; i++) { + // Non-zero elements on this processor + d_nnz[i] = 9; // Square pattern in 2D + // Non-zero elements on neighboring processor + o_nnz[i] = 0; + } + + // X boundaries + if (localmesh->firstX()) { + // Lower X boundary + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int localIndex = globalIndex(localmesh->xstart - 1, y); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + + d_nnz[localIndex] = 2; // Diagonal sub-matrix + o_nnz[localIndex] = 0; // Off-diagonal sub-matrix + } + } else { + // On another processor + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int localIndex = globalIndex(localmesh->xstart, y); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] -= 3; + o_nnz[localIndex] += 3; + } + } + if (localmesh->lastX()) { + // Upper X boundary + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int localIndex = globalIndex(localmesh->xend + 1, y); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = 2; // Diagonal sub-matrix + o_nnz[localIndex] = 0; // Off-diagonal sub-matrix + } + } else { + // On another processor + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int localIndex = globalIndex(localmesh->xend, y); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] -= 3; + o_nnz[localIndex] += 3; + } + } + // Y boundaries + const int y_bndry_stencil_size = (y_bndry == "free_o3") ? 4 : 2; + + for (int x = localmesh->xstart; x <= localmesh->xend; x++) { + // Default to no boundary + // NOTE: This assumes that communications in Y are to other + // processors. If Y is communicated with this processor (e.g. NYPE=1) + // then this will result in PETSc warnings about out of range allocations + { + const int localIndex = globalIndex(x, localmesh->ystart); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + // d_nnz[localIndex] -= 3; // Note: Slightly inefficient + o_nnz[localIndex] += 3; + } + { + const int localIndex = globalIndex(x, localmesh->yend); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + // d_nnz[localIndex] -= 3; // Note: Slightly inefficient + o_nnz[localIndex] += 3; + } + } + + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, they are handled specially below + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + { + const int localIndex = globalIndex(it.ind, localmesh->ystart - 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = y_bndry_stencil_size; // Diagonal sub-matrix + o_nnz[localIndex] = 0; // Off-diagonal sub-matrix + } + { + const int localIndex = globalIndex(it.ind, localmesh->ystart); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + //d_nnz[localIndex] += 3; + o_nnz[localIndex] -= 3; + } + } + if (localmesh->hasBndryLowerY()) { + if (y_bndry == "dirichlet") { + // special handling for the corners, since we use a free_o3 y-boundary + // condition just in the corners when y_bndry=="dirichlet" + if (localmesh->firstX()) { + const int localIndex = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = 4; + } + if (localmesh->lastX()) { + const int localIndex = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = 4; + } + } else { + if (localmesh->firstX()) { + const int localIndex = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = y_bndry_stencil_size; + } + if (localmesh->lastX()) { + const int localIndex = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = y_bndry_stencil_size; + } + } + } + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, they are handled specially below + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + { + const int localIndex = globalIndex(it.ind, localmesh->yend + 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = y_bndry_stencil_size; // Diagonal sub-matrix + o_nnz[localIndex] = 0; // Off-diagonal sub-matrix + } + { + const int localIndex = globalIndex(it.ind, localmesh->yend); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + //d_nnz[localIndex] += 3; + o_nnz[localIndex] -= 3; + } + } + if (localmesh->hasBndryUpperY()) { + if (y_bndry == "dirichlet") { + // special handling for the corners, since we use a free_o3 y-boundary + // condition just in the corners when y_bndry=="dirichlet" + if (localmesh->firstX()) { + const int localIndex = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = 4; + } + if (localmesh->lastX()) { + const int localIndex = globalIndex(localmesh->xend + 1, localmesh->yend + 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = 4; + } + } else { + if (localmesh->firstX()) { + const int localIndex = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = y_bndry_stencil_size; + } + if (localmesh->lastX()) { + const int localIndex = globalIndex(localmesh->xend + 1, localmesh->yend + 1); + ASSERT1((localIndex >= 0) && (localIndex < localN)); + d_nnz[localIndex] = y_bndry_stencil_size; + } + } + } +} + +void LaplaceXYpetsc::setCoefs(const Field2D& A, const Field2D& B) { + Timer timer("invert"); + + ASSERT1(A.getMesh() == localmesh); + ASSERT1(B.getMesh() == localmesh); + ASSERT1(A.getLocation() == location); + ASSERT1(B.getLocation() == location); + + if (finite_volume) { + setMatrixElementsFiniteVolume(A, B); + } else { + setMatrixElementsFiniteDifference(A, B); + } + + // X boundaries + if (localmesh->firstX()) { + if (x_inner_dirichlet) { + + // Dirichlet on inner X boundary + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int row = globalIndex(localmesh->xstart - 1, y); + PetscScalar val = 0.5; + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + int col = globalIndex(localmesh->xstart, y); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + // Preconditioner + bcoef(y - localmesh->ystart, 0) = 0.5; + ccoef(y - localmesh->ystart, 0) = 0.5; + } + + } else { + + // Neumann on inner X boundary + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int row = globalIndex(localmesh->xstart - 1, y); + PetscScalar val = 1.0; + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + int col = globalIndex(localmesh->xstart, y); + val = -1.0; + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + // Preconditioner + bcoef(y - localmesh->ystart, 0) = 1.0; + ccoef(y - localmesh->ystart, 0) = -1.0; + } + } + } + if (localmesh->lastX()) { + // Dirichlet on outer X boundary + + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int row = globalIndex(localmesh->xend + 1, y); + PetscScalar val = 0.5; + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + int col = globalIndex(localmesh->xend, y); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + // Preconditioner + acoef(y - localmesh->ystart, localmesh->xend + 1 - xstart) = 0.5; + bcoef(y - localmesh->ystart, localmesh->xend + 1 - xstart) = 0.5; + } + } + + if (y_bndry == "dirichlet") { + // Dirichlet on Y boundaries + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int row = globalIndex(it.ind, localmesh->ystart - 1); + PetscScalar val = 0.5; + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + int col = globalIndex(it.ind, localmesh->ystart); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } + + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int row = globalIndex(it.ind, localmesh->yend + 1); + PetscScalar val = 0.5; + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + int col = globalIndex(it.ind, localmesh->yend); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } + } else if (y_bndry == "neumann") { + // Neumann on Y boundaries + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int row = globalIndex(it.ind, localmesh->ystart - 1); + PetscScalar val = 1.0; + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -1.0; + int col = globalIndex(it.ind, localmesh->ystart); + + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } + + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int row = globalIndex(it.ind, localmesh->yend + 1); + PetscScalar val = 1.0; + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -1.0; + int col = globalIndex(it.ind, localmesh->yend); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } + } else if (y_bndry == "free_o3") { + // 'free_o3' extrapolating boundary condition on Y boundaries + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int row = globalIndex(it.ind, localmesh->ystart - 1); + PetscScalar val = 1.0; + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -3.0; + int col = globalIndex(it.ind, localmesh->ystart); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = 3.0; + col = globalIndex(it.ind, localmesh->ystart + 1); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = -1.0; + col = globalIndex(it.ind, localmesh->ystart + 2); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } + + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int row = globalIndex(it.ind, localmesh->yend + 1); + PetscScalar val = 1.0; + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -3.0; + int col = globalIndex(it.ind, localmesh->yend); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = 3.0; + col = globalIndex(it.ind, localmesh->yend - 1); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = -1.0; + col = globalIndex(it.ind, localmesh->yend - 2); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } + } else { + throw BoutException("Unsupported option for y_bndry"); + } + + if (not finite_volume) { + // Handle corner boundary cells in case we need to include D2DXDY + // Apply the y-boundary-condition to the cells in the x-boundary - this is an + // arbitrary choice, cf. connections around the X-point + + if (localmesh->hasBndryLowerY()) { + if (localmesh->firstX()) { + if (y_bndry == "neumann") { + // Neumann y-bc + // f(xs-1,ys-1) = f(xs-1,ys) + PetscScalar val = 1.0; + int row = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -1.0; + int col = globalIndex(localmesh->xstart - 1, localmesh->ystart); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } else if (y_bndry == "free_o3" or y_bndry == "dirichlet") { + // 'free_o3' extrapolating boundary condition on Y boundaries + // f(xs-1,ys-1) = 3*f(xs-1,ys) - 3*f(xs-1,ys+1) + f(xs-1,ys+2) + // + // Use free_o3 at the corners for Dirichlet y-boundaries because we don't know + // what value to pass for the corner + PetscScalar val = 1.0; + int row = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -3.0; + int col = globalIndex(localmesh->xstart - 1, localmesh->ystart); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = 3.0; + col = globalIndex(localmesh->xstart - 1, localmesh->ystart + 1); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = -1.0; + col = globalIndex(localmesh->xstart - 1, localmesh->ystart + 2); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } else { + throw BoutException("Unsupported option for y_bndry"); + } + } + if (localmesh->lastX()) { + if (y_bndry == "neumann") { + // Neumann y-bc + // f(xe+1,ys-1) = f(xe+1,ys) + PetscScalar val = 1.0; + int row = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -1.0; + int col = globalIndex(localmesh->xend + 1, localmesh->ystart); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } else if (y_bndry == "free_o3" or y_bndry == "dirichlet") { + // 'free_o3' extrapolating boundary condition on Y boundaries + // f(xe+1,ys-1) = 3*f(xe+1,ys) - 3*f(xe+1,ys+1) + f(xe+1,ys+2) + // + // Use free_o3 at the corners for Dirichlet y-boundaries because we don't know + // what value to pass for the corner + PetscScalar val = 1.0; + int row = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -3.0; + int col = globalIndex(localmesh->xend + 1, localmesh->ystart); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = 3.0; + col = globalIndex(localmesh->xend + 1, localmesh->ystart + 1); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = -1.0; + col = globalIndex(localmesh->xend + 1, localmesh->ystart + 2); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } else { + throw BoutException("Unsupported option for y_bndry"); + } + } + } + if (localmesh->hasBndryUpperY()) { + if (localmesh->firstX()) { + if (y_bndry == "neumann") { + // Neumann y-bc + // f(xs-1,ys-1) = f(xs-1,ys) + PetscScalar val = 1.0; + int row = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -1.0; + int col = globalIndex(localmesh->xstart - 1, localmesh->yend); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } else if (y_bndry == "free_o3" or y_bndry == "dirichlet") { + // 'free_o3' extrapolating boundary condition on Y boundaries + // f(xs-1,ys-1) = 3*f(xs-1,ys) - 3*f(xs-1,ys+1) + f(xs-1,ys+2) + // + // Use free_o3 at the corners for Dirichlet y-boundaries because we don't know + // what value to pass for the corner + PetscScalar val = 1.0; + int row = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -3.0; + int col = globalIndex(localmesh->xstart - 1, localmesh->yend); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = 3.0; + col = globalIndex(localmesh->xstart - 1, localmesh->yend - 1); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = -1.0; + col = globalIndex(localmesh->xstart - 1, localmesh->yend - 2); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } else { + throw BoutException("Unsupported option for y_bndry"); + } + } + if (localmesh->lastX()) { + if (y_bndry == "neumann") { + // Neumann y-bc + // f(xe+1,ys-1) = f(xe+1,ys) + PetscScalar val = 1.0; + int row = globalIndex(localmesh->xend + 1, localmesh->yend + 1); + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -1.0; + int col = globalIndex(localmesh->xend + 1, localmesh->yend); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } else if (y_bndry == "free_o3" or y_bndry == "dirichlet") { + // 'free_o3' extrapolating boundary condition on Y boundaries + // f(xe+1,ys-1) = 3*f(xe+1,ys) - 3*f(xe+1,ys+1) + f(xe+1,ys+2) + // + // Use free_o3 at the corners for Dirichlet y-boundaries because we don't know + // what value to pass for the corner + PetscScalar val = 1.0; + int row = globalIndex(localmesh->xend + 1, localmesh->yend + 1); + MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); + + val = -3.0; + int col = globalIndex(localmesh->xend + 1, localmesh->yend); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = 3.0; + col = globalIndex(localmesh->xend + 1, localmesh->yend - 1); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + + val = -1.0; + col = globalIndex(localmesh->xend + 1, localmesh->yend - 2); + MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); + } else { + throw BoutException("Unsupported option for y_bndry"); + } + } + } + } + + // Assemble Matrix + MatAssemblyBegin(MatA, MAT_FINAL_ASSEMBLY); + MatAssemblyEnd(MatA, MAT_FINAL_ASSEMBLY); + + // Set the operator +#if PETSC_VERSION_GE(3, 5, 0) + KSPSetOperators(ksp, MatA, MatA); +#else + KSPSetOperators(ksp, MatA, MatA, DIFFERENT_NONZERO_PATTERN); +#endif + + // Set coefficients for preconditioner + cr->setCoefs(acoef, bcoef, ccoef); +} + +void LaplaceXYpetsc::setMatrixElementsFiniteVolume(const Field2D& A, const Field2D& B) { + ////////////////////////////////////////////////// + // Set Matrix elements + // + // (1/J) d/dx ( J * g11 d/dx ) + (1/J) d/dy ( J * g22 d/dy ) + + auto coords = localmesh->getCoordinates(location); + const Field2D J_DC = DC(coords->J); + const Field2D g11_DC = DC(coords->g11); + const Field2D dx_DC = DC(coords->dx); + const Field2D dy_DC = DC(coords->dy); + const Field2D g_22_DC = DC(coords->g_22); + const Field2D g_23_DC = DC(coords->g_23); + const Field2D g23_DC = DC(coords->g23); + + for (int x = localmesh->xstart; x <= localmesh->xend; x++) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + // stencil entries + PetscScalar c, xm, xp, ym, yp; + + // XX component + + // Metrics on x+1/2 boundary + BoutReal J = 0.5 * (J_DC(x, y) + J_DC(x + 1, y)); + BoutReal g11 = 0.5 * (g11_DC(x, y) + g11_DC(x + 1, y)); + BoutReal dx = 0.5 * (dx_DC(x, y) + dx_DC(x + 1, y)); + BoutReal Acoef = 0.5 * (A(x, y) + A(x + 1, y)); + + BoutReal val = Acoef * J * g11 / (J_DC(x, y) * dx * dx_DC(x, y)); + xp = val; + c = -val; + + // Metrics on x-1/2 boundary + J = 0.5 * (J_DC(x, y) + J_DC(x - 1, y)); + g11 = 0.5 * (g11_DC(x, y) + g11_DC(x - 1, y)); + dx = 0.5 * (dx_DC(x, y) + dx_DC(x - 1, y)); + Acoef = 0.5 * (A(x, y) + A(x - 1, y)); + + val = Acoef * J * g11 / (J_DC(x, y) * dx * dx_DC(x, y)); + xm = val; + c -= val; + + c += B(x, y); + + // Put values into the preconditioner, X derivatives only + acoef(y - localmesh->ystart, x - xstart) = xm; + bcoef(y - localmesh->ystart, x - xstart) = c; + ccoef(y - localmesh->ystart, x - xstart) = xp; + + if (include_y_derivs) { + // YY component + // Metrics at y+1/2 + J = 0.5 * (J_DC(x, y) + J_DC(x, y + 1)); + BoutReal g_22 = 0.5 * (g_22_DC(x, y) + g_22_DC(x, y + 1)); + BoutReal g23 = 0.5 * (g23_DC(x, y) + g23_DC(x, y + 1)); + BoutReal g_23 = 0.5 * (g_23_DC(x, y) + g_23_DC(x, y + 1)); + BoutReal dy = 0.5 * (dy_DC(x, y) + dy_DC(x, y + 1)); + Acoef = 0.5 * (A(x, y + 1) + A(x, y)); + + val = -Acoef * J * g23 * g_23 / (g_22 * J_DC(x, y) * dy * dy_DC(x, y)); + yp = val; + c -= val; + + // Metrics at y-1/2 + J = 0.5 * (J_DC(x, y) + J_DC(x, y - 1)); + g_22 = 0.5 * (g_22_DC(x, y) + g_22_DC(x, y - 1)); + g23 = 0.5 * (g23_DC(x, y) + g23_DC(x, y - 1)); + g_23 = 0.5 * (g_23_DC(x, y) + g_23_DC(x, y - 1)); + dy = 0.5 * (dy_DC(x, y) + dy_DC(x, y - 1)); + Acoef = 0.5 * (A(x, y - 1) + A(x, y)); + + val = -Acoef * J * g23 * g_23 / (g_22 * J_DC(x, y) * dy * dy_DC(x, y)); + ym = val; + c -= val; + } + + ///////////////////////////////////////////////// + // Now have a 5-point stencil for the Laplacian + + int row = globalIndex(x, y); + + // Set the centre (diagonal) + MatSetValues(MatA, 1, &row, 1, &row, &c, INSERT_VALUES); + + // X + 1 + int col = globalIndex(x + 1, y); + MatSetValues(MatA, 1, &row, 1, &col, &xp, INSERT_VALUES); + + // X - 1 + col = globalIndex(x - 1, y); + MatSetValues(MatA, 1, &row, 1, &col, &xm, INSERT_VALUES); + + if (include_y_derivs) { + // Y + 1 + col = globalIndex(x, y + 1); + MatSetValues(MatA, 1, &row, 1, &col, &yp, INSERT_VALUES); + + // Y - 1 + col = globalIndex(x, y - 1); + MatSetValues(MatA, 1, &row, 1, &col, &ym, INSERT_VALUES); + } + } + } +} + +void LaplaceXYpetsc::setMatrixElementsFiniteDifference(const Field2D& A, + const Field2D& B) { + ////////////////////////////////////////////////// + // Set Matrix elements + // + // Div(A Grad(f)) + B f + // = A Laplace_perp(f) + Grad_perp(A).Grad_perp(f) + B f + // = A*(G1*dfdx + (G2-1/J*d/dy(J/g_22))*dfdy + // + g11*d2fdx2 + (g22-1/g_22)*d2fdy2 + 2*g12*d2fdxdy) + // + g11*dAdx*dfdx + (g22-1/g_22)*dAdy*dfdy + g12*(dAdx*dfdy + dAdy*dfdx) + // + B*f + + auto coords = localmesh->getCoordinates(location); + const Field2D G1_2D = DC(coords->G1); + const Field2D G2_2D = DC(coords->G2); + const Field2D J_2D = DC(coords->J); + const Field2D g11_2D = DC(coords->g11); + const Field2D g_22_2D = DC(coords->g_22); + const Field2D g22_2D = DC(coords->g22); + const Field2D g12_2D = DC(coords->g12); + const Field2D d1_dx_2D = DC(coords->d1_dx); + const Field2D d1_dy_2D = DC(coords->d1_dy); + const Field2D dx_2D = DC(coords->dx); + const Field2D dy_2D = DC(coords->dy); + + const Field2D coef_dfdy = G2_2D - DC(DDY(J_2D / g_22_2D) / J_2D); + + for (int x = localmesh->xstart; x <= localmesh->xend; x++) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + // stencil entries + PetscScalar c, xm, xp, ym, yp, xpyp, xpym, xmyp, xmym; + + BoutReal dx = dx_2D(x, y); + + // A*G1*dfdx + BoutReal val = A(x, y) * G1_2D(x, y) / (2. * dx); + xp = val; + xm = -val; + + // A*g11*d2fdx2 + val = A(x, y) * g11_2D(x, y) / SQ(dx); + xp += val; + c = -2. * val; + xm += val; + // Non-uniform grid correction + val = A(x, y) * g11_2D(x, y) * d1_dx_2D(x, y) / (2. * dx); + xp += val; + xm -= val; + + // g11*dAdx*dfdx + val = g11_2D(x, y) * (A(x + 1, y) - A(x - 1, y)) / (4. * SQ(dx)); + xp += val; + xm -= val; + + // B*f + c += B(x, y); + + // Put values into the preconditioner, X derivatives only + acoef(y - localmesh->ystart, x - xstart) = xm; + bcoef(y - localmesh->ystart, x - xstart) = c; + ccoef(y - localmesh->ystart, x - xstart) = xp; + + if (include_y_derivs) { + BoutReal dy = dy_2D(x, y); + BoutReal dAdx = (A(x + 1, y) - A(x - 1, y)) / (2. * dx); + BoutReal dAdy = (A(x, y + 1) - A(x, y - 1)) / (2. * dy); + + // A*(G2-1/J*d/dy(J/g_22))*dfdy + val = A(x, y) * coef_dfdy(x, y) / (2. * dy); + yp = val; + ym = -val; + + // A*(g22-1/g_22)*d2fdy2 + val = A(x, y) * (g22_2D(x, y) - 1. / g_22_2D(x, y)) / SQ(dy); + yp += val; + c -= 2. * val; + ym += val; + // Non-uniform mesh correction + val = A(x, y) * (g22_2D(x, y) - 1. / g_22_2D(x, y)) * d1_dy_2D(x, y) / (2. * dy); + yp += val; + ym -= val; + + // 2*A*g12*d2dfdxdy + val = A(x, y) * g12_2D(x, y) / (2. * dx * dy); + xpyp = val; + xpym = -val; + xmyp = -val; + xmym = val; + + // g22*dAdy*dfdy + val = (g22_2D(x, y) - 1. / g_22_2D(x, y)) * dAdy / (2. * dy); + yp += val; + ym -= val; + + // g12*(dAdx*dfdy + dAdy*dfdx) + val = g12_2D(x, y) * dAdx / (2. * dy); + yp += val; + ym -= val; + val = g12_2D(x, y) * dAdy / (2. * dx); + xp += val; + xm -= val; + } + + ///////////////////////////////////////////////// + // Now have a 9-point stencil for the Laplacian + + int row = globalIndex(x, y); + + // Set the centre (diagonal) + MatSetValues(MatA, 1, &row, 1, &row, &c, INSERT_VALUES); + + // X + 1 + int col = globalIndex(x + 1, y); + MatSetValues(MatA, 1, &row, 1, &col, &xp, INSERT_VALUES); + + // X - 1 + col = globalIndex(x - 1, y); + MatSetValues(MatA, 1, &row, 1, &col, &xm, INSERT_VALUES); + + if (include_y_derivs) { + // Y + 1 + col = globalIndex(x, y + 1); + MatSetValues(MatA, 1, &row, 1, &col, &yp, INSERT_VALUES); + + // Y - 1 + col = globalIndex(x, y - 1); + MatSetValues(MatA, 1, &row, 1, &col, &ym, INSERT_VALUES); + + // X + 1, Y + 1 + col = globalIndex(x + 1, y + 1); + MatSetValues(MatA, 1, &row, 1, &col, &xpyp, INSERT_VALUES); + + // X + 1, Y - 1 + col = globalIndex(x + 1, y - 1); + MatSetValues(MatA, 1, &row, 1, &col, &xpym, INSERT_VALUES); + + // X - 1, Y + 1 + col = globalIndex(x - 1, y + 1); + MatSetValues(MatA, 1, &row, 1, &col, &xmyp, INSERT_VALUES); + + // X - 1, Y - 1 + col = globalIndex(x - 1, y - 1); + MatSetValues(MatA, 1, &row, 1, &col, &xmym, INSERT_VALUES); + } + } + } +} + +LaplaceXYpetsc::~LaplaceXYpetsc() { + PetscBool is_finalised; + PetscFinalized(&is_finalised); + + if (!is_finalised) { + // PetscFinalize may already have destroyed this object + KSPDestroy(&ksp); + } + + VecDestroy(&xs); + VecDestroy(&bs); + MatDestroy(&MatA); +} + +Field2D LaplaceXYpetsc::solve(const Field2D& rhs, const Field2D& x0) { + Timer timer("invert"); + + ASSERT1(rhs.getMesh() == localmesh); + ASSERT1(x0.getMesh() == localmesh); + ASSERT1(rhs.getLocation() == location); + ASSERT1(x0.getLocation() == location); + + // Load initial guess x0 into xs and rhs into bs + + for (int x = localmesh->xstart; x <= localmesh->xend; x++) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(x, y); + + PetscScalar val = x0(x, y); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + val = rhs(x, y); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + + if (finite_volume) { + solveFiniteVolume(x0); + } else { + solveFiniteDifference(x0); + } + + // Assemble RHS Vector + VecAssemblyBegin(bs); + VecAssemblyEnd(bs); + + // Assemble Trial Solution Vector + VecAssemblyBegin(xs); + VecAssemblyEnd(xs); + + // Solve the system + KSPSolve(ksp, bs, xs); + + KSPConvergedReason reason; + KSPGetConvergedReason(ksp, &reason); + + if (reason <= 0) { + throw BoutException("LaplaceXYpetsc failed to converge. Reason {} ({:d})", + KSPConvergedReasons[reason], static_cast(reason)); + } + + if (save_performance) { + // Update performance monitoring information + n_calls++; + + int iterations = 0; + KSPGetIterationNumber(ksp, &iterations); + + average_iterations = BoutReal(n_calls - 1) / BoutReal(n_calls) * average_iterations + + BoutReal(iterations) / BoutReal(n_calls); + } + + ////////////////////////// + // Copy data into result + + Field2D result; + result.allocate(); + result.setLocation(location); + + for (int x = localmesh->xstart; x <= localmesh->xend; x++) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(x, y); + + PetscScalar val; + VecGetValues(xs, 1, &ind, &val); + result(x, y) = val; + } + } + + // Inner X boundary + if (localmesh->firstX()) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(localmesh->xstart - 1, y); + PetscScalar val; + VecGetValues(xs, 1, &ind, &val); + for (int x = localmesh->xstart - 1; x >= 0; x--) { + result(x, y) = val; + } + } + } + + // Outer X boundary + if (localmesh->lastX()) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(localmesh->xend + 1, y); + PetscScalar val; + VecGetValues(xs, 1, &ind, &val); + for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { + result(x, y) = val; + } + } + } + + // Lower Y boundary + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + if ( + // Should not go into corner cells, finite-volume LaplaceXYpetsc stencil does not include + // them + (finite_volume and (it.ind < localmesh->xstart or it.ind > localmesh->xend)) + + // Only go into first corner cell for finite-difference + or (it.ind < localmesh->xstart - 1 or it.ind > localmesh->xend + 1)) { + continue; + } + int ind = globalIndex(it.ind, localmesh->ystart - 1); + PetscScalar val; + VecGetValues(xs, 1, &ind, &val); + for (int y = localmesh->ystart - 1; y >= 0; y--) { + result(it.ind, y) = val; + } + } + + // Upper Y boundary + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + if ( + // Should not go into corner cells, finite-volume LaplaceXYpetsc stencil does not include + // them + (finite_volume and (it.ind < localmesh->xstart or it.ind > localmesh->xend)) + + // Only go into first corner cell for finite-difference + or (it.ind < localmesh->xstart - 1 or it.ind > localmesh->xend + 1)) { + continue; + } + int ind = globalIndex(it.ind, localmesh->yend + 1); + PetscScalar val; + VecGetValues(xs, 1, &ind, &val); + for (int y = localmesh->yend + 1; y < localmesh->LocalNy; y++) { + result(it.ind, y) = val; + } + } + + return result; +} + +void LaplaceXYpetsc::solveFiniteVolume(const Field2D& x0) { + // Use original LaplaceXYpetsc implementation of passing boundary values for backward + // compatibility + if (localmesh->firstX()) { + if (x_inner_dirichlet) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(localmesh->xstart - 1, y); + + PetscScalar val = x0(localmesh->xstart - 1, y); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + val = 0.5 * (x0(localmesh->xstart - 1, y) + x0(localmesh->xstart, y)); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } else { + // Inner X boundary (Neumann) + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(localmesh->xstart - 1, y); + + PetscScalar val = x0(localmesh->xstart - 1, y); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + val = 0.0; // x0(localmesh->xstart-1,y) - x0(localmesh->xstart,y); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + } + + // Outer X boundary (Dirichlet) + if (localmesh->lastX()) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(localmesh->xend + 1, y); + + PetscScalar val = x0(localmesh->xend + 1, y); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + val = 0.5 * (x0(localmesh->xend, y) + x0(localmesh->xend + 1, y)); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + + if (y_bndry == "dirichlet") { + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->ystart - 1); + + PetscScalar val = x0(it.ind, localmesh->ystart - 1); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + val = 0.5 * (x0(it.ind, localmesh->ystart - 1) + x0(it.ind, localmesh->ystart)); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->yend + 1); + + PetscScalar val = x0(it.ind, localmesh->yend + 1); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + val = 0.5 * (x0(it.ind, localmesh->yend + 1) + x0(it.ind, localmesh->yend)); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } else if (y_bndry == "neumann" or y_bndry == "free_o3") { + // Y boundaries Neumann + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->ystart - 1); + + PetscScalar val = x0(it.ind, localmesh->ystart - 1); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->yend + 1); + + PetscScalar val = x0(it.ind, localmesh->yend + 1); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } else { + throw BoutException("Unsupported option for y_bndry"); + } +} + +void LaplaceXYpetsc::solveFiniteDifference(const Field2D& x0) { + // For finite-difference implementation pass boundary values in the same way as for + // the 'Laplacian' solvers - the value to use (for Dirichlet boundary conditions) on + // the boundary (which is half way between grid cell and boundary cell) is passed as + // the value in the first boundary cell. + if (localmesh->firstX()) { + if (x_inner_dirichlet) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(localmesh->xstart - 1, y); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 2. * x0(localmesh->xstart - 1, y) - x0(localmesh->xstart, y); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Pass the value from boundary cell of x0 as the boundary condition to the rhs + val = x0(localmesh->xstart - 1, y); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } else { + // Inner X boundary (Neumann) + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(localmesh->xstart - 1, y); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = x0(localmesh->xstart, y); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + } + + // Outer X boundary (Dirichlet) + if (localmesh->lastX()) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + int ind = globalIndex(localmesh->xend + 1, y); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 2. * x0(localmesh->xend + 1, y) - x0(localmesh->xend, y); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Pass the value from boundary cell of x0 as the boundary condition to the rhs + val = x0(localmesh->xend + 1, y); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + + if (y_bndry == "dirichlet") { + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, they are treated specially below + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->ystart - 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = + 2. * x0(it.ind, localmesh->ystart - 1) - x0(it.ind, localmesh->ystart); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Pass the value from boundary cell of x0 as the boundary condition to the rhs + val = x0(it.ind, localmesh->ystart - 1); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, they are treated specially below + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->yend + 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = + 2. * x0(it.ind, localmesh->yend + 1) - x0(it.ind, localmesh->yend); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Pass the value from boundary cell of x0 as the boundary condition to the rhs + val = x0(it.ind, localmesh->yend + 1); + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + + // Use free_o3 for the corner boundary cells + if (localmesh->hasBndryLowerY()) { + if (localmesh->firstX()) { + int ind = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(localmesh->xstart - 1, localmesh->ystart) + - 3. * x0(localmesh->xstart - 1, localmesh->ystart + 1) + + x0(localmesh->xstart - 1, localmesh->ystart + 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->lastX()) { + int ind = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(localmesh->xend + 1, localmesh->ystart) + - 3. * x0(localmesh->xend + 1, localmesh->ystart + 1) + + x0(localmesh->xend + 1, localmesh->ystart + 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + if (localmesh->hasBndryUpperY()) { + if (localmesh->firstX()) { + int ind = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(localmesh->xstart - 1, localmesh->yend) + - 3. * x0(localmesh->xstart - 1, localmesh->yend - 1) + + x0(localmesh->xstart - 1, localmesh->yend - 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->lastX()) { + int ind = globalIndex(localmesh->xend + 1, localmesh->yend + 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(localmesh->xend + 1, localmesh->yend) + - 3. * x0(localmesh->xend + 1, localmesh->yend - 1) + + x0(localmesh->xend + 1, localmesh->yend - 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + } else if (y_bndry == "neumann") { + // Y boundaries Neumann + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, they are treated specially below + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->ystart - 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = x0(it.ind, localmesh->ystart); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->hasBndryLowerY()) { + if (localmesh->firstX()) { + int ind = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = x0(localmesh->xstart - 1, localmesh->ystart); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->lastX()) { + int ind = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = x0(localmesh->xend + 1, localmesh->ystart); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, they are treated specially below + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->yend + 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = x0(it.ind, localmesh->yend); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->hasBndryUpperY()) { + if (localmesh->firstX()) { + int ind = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = x0(localmesh->xstart - 1, localmesh->yend); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->lastX()) { + int ind = globalIndex(localmesh->xend + 1, localmesh->yend + 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = x0(localmesh->xend + 1, localmesh->yend); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + } else if (y_bndry == "free_o3") { + // Y boundaries free_o3 + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, they are treated specially below + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->ystart - 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(it.ind, localmesh->ystart) + - 3. * x0(it.ind, localmesh->ystart + 1) + + x0(it.ind, localmesh->ystart + 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->hasBndryLowerY()) { + if (localmesh->firstX()) { + int ind = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(localmesh->xstart - 1, localmesh->ystart) + - 3. * x0(localmesh->xstart - 1, localmesh->ystart + 1) + + x0(localmesh->xstart - 1, localmesh->ystart + 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->lastX()) { + int ind = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(localmesh->xend + 1, localmesh->ystart) + - 3. * x0(localmesh->xend + 1, localmesh->ystart + 1) + + x0(localmesh->xend + 1, localmesh->ystart + 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, they are treated specially below + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + int ind = globalIndex(it.ind, localmesh->yend + 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(it.ind, localmesh->yend) + - 3. * x0(it.ind, localmesh->yend - 1) + + x0(it.ind, localmesh->yend - 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->hasBndryUpperY()) { + if (localmesh->firstX()) { + int ind = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(localmesh->xstart - 1, localmesh->yend) + - 3. * x0(localmesh->xstart - 1, localmesh->yend - 1) + + x0(localmesh->xstart - 1, localmesh->yend - 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + if (localmesh->lastX()) { + int ind = globalIndex(localmesh->xend + 1, localmesh->yend + 1); + + // For the boundary value of the initial guess, use the value that would be set by + // applying the boundary condition to the initial guess + PetscScalar val = 3. * x0(localmesh->xend + 1, localmesh->yend) + - 3. * x0(localmesh->xend + 1, localmesh->yend - 1) + + x0(localmesh->xend + 1, localmesh->yend - 2); + VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); + + // Set the value for the rhs of the boundary condition to zero + val = 0.0; + VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); + } + } + } else { + throw BoutException("Unsupported option for y_bndry"); + } +} + +/*! Preconditioner + * NOTE: For generality, this routine does use globalIndex() in the inner loop, although + * this may be slightly less efficient than incrementing an integer for the global index, + * the finite-volume and finite-difference implementations have slightly different + * indexing patterns, so incrementing an integer would be tricky. + */ +int LaplaceXYpetsc::precon(Vec input, Vec result) { + + for (auto itdwn = localmesh->iterateBndryLowerY(); !itdwn.isDone(); itdwn++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (itdwn.ind < localmesh->xstart or itdwn.ind > localmesh->xend) { + continue; + } + const int ind = globalIndex(itdwn.ind, localmesh->ystart - 1); + PetscScalar val; + VecGetValues(input, 1, &ind, &val); + VecSetValues(result, 1, &ind, &val, INSERT_VALUES); + } + for (auto itup = localmesh->iterateBndryUpperY(); !itup.isDone(); itup++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (itup.ind < localmesh->xstart or itup.ind > localmesh->xend) { + continue; + } + const int ind = globalIndex(itup.ind, localmesh->yend + 1); + PetscScalar val; + VecGetValues(input, 1, &ind, &val); + VecSetValues(result, 1, &ind, &val, INSERT_VALUES); + } + + // Load vector x into bvals array + for (int x = xstart; x <= xend; x++) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int ind = globalIndex(x, y); + PetscScalar val; + VecGetValues(input, 1, &ind, &val); + bvals(y - localmesh->ystart, x - xstart) = val; + } + } + + // Solve tridiagonal systems using CR solver + cr->solve(bvals, xvals); + + // Save result xvals into y array + for (int x = xstart; x <= xend; x++) { + for (int y = localmesh->ystart; y <= localmesh->yend; y++) { + const int ind = globalIndex(x, y); + PetscScalar val = xvals(y - localmesh->ystart, x - xstart); + VecSetValues(result, 1, &ind, &val, INSERT_VALUES); + } + } + VecAssemblyBegin(result); + VecAssemblyEnd(result); + return 0; +} + +/////////////////////////////////////////////////////////////// + +int LaplaceXYpetsc::localSize() { + + // Bulk of points + const int nx = localmesh->xend - localmesh->xstart + 1; + const int ny = localmesh->yend - localmesh->ystart + 1; + + int n = nx * ny; + + // X boundaries + if (localmesh->firstX()) { + n += ny; + } + if (localmesh->lastX()) { + n += ny; + } + + // Y boundaries + for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + n++; + } + if ((not finite_volume) and localmesh->hasBndryLowerY()) { + if (localmesh->firstX()) { + n++; + } + if (localmesh->lastX()) { + n++; + } + } + for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { + // Should not go into corner cells, LaplaceXYpetsc stencil does not include them + if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { + continue; + } + n++; + } + if ((not finite_volume) and localmesh->hasBndryUpperY()) { + if (localmesh->firstX()) { + n++; + } + if (localmesh->lastX()) { + n++; + } + } + + return n; +} + +int LaplaceXYpetsc::globalIndex(int x, int y) { + if ((x < 0) || (x >= localmesh->LocalNx) || (y < 0) || (y >= localmesh->LocalNy)) { + return -1; // Out of range + } + + // Get the index from a Field2D, round to integer + return static_cast(std::round(indexXY(x, y))); +} + +void LaplaceXYpetsc::savePerformance(Solver& solver, const std::string& name) { + // set flag so that performance monitoring values are calculated + save_performance = true; + + // add values to be saved to the output + if (not name.empty()) { + default_prefix = name; + } + + // add monitor to reset counters/averages for new output timestep + // monitor added to back of queue, so that values are reset after being saved + solver.addMonitor(&monitor, Solver::BACK); +} + +int LaplaceXYpetsc::LaplaceXYMonitor::call(Solver* /*solver*/, BoutReal /*time*/, + int /*iter*/, int /*nout*/) { + laplacexy->output_average_iterations = laplacexy->average_iterations; + + laplacexy->n_calls = 0; + laplacexy->average_iterations = 0.; + + return 0; +} + +void LaplaceXYpetsc::LaplaceXYMonitor::outputVars(Options& output_options, + const std::string& time_dimension) { + output_options[fmt::format("{}_average_iterations", laplacexy->default_prefix)] + .assignRepeat(laplacexy->output_average_iterations, time_dimension); +} + +#endif // BOUT_HAS_PETSC diff --git a/src/invert/laplacexy/impls/petsc/laplacexy-petsc.hxx b/src/invert/laplacexy/impls/petsc/laplacexy-petsc.hxx new file mode 100644 index 0000000000..27d25cf9a7 --- /dev/null +++ b/src/invert/laplacexy/impls/petsc/laplacexy-petsc.hxx @@ -0,0 +1,209 @@ +/************************************************************************** + * Laplacian solver in 2D (X-Y) + * + * Equation solved is: + * + * Div( A * Grad_perp(x) ) + B*x = b + * + * Intended for use in solving n = 0 component of potential + * from inversion of vorticity equation + * + ************************************************************************** + * Copyright 2015 B.Dudson + * + * Contact: Ben Dudson, bd512@york.ac.uk + * + * This file is part of BOUT++. + * + * BOUT++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * BOUT++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with BOUT++. If not, see . + * + **************************************************************************/ + +#ifndef BOUT_LAPLACE_XY_PETSC_H +#define BOUT_LAPLACE_XY_PETSC_H + +#include "bout/build_defines.hxx" +#include "bout/invert/laplacexy.hxx" + +#if !BOUT_HAS_PETSC + +namespace { +RegisterUnavailableLaplaceXY + registerlaplacexypetsc("petsc", "BOUT++ was not configured with PETSc"); +} + +#else // BOUT_HAS_PETSC + +#include "bout/bout_types.hxx" +#include "bout/cyclic_reduction.hxx" +#include "bout/field2d.hxx" +#include "bout/mesh.hxx" +#include "bout/monitor.hxx" +#include "bout/petsclib.hxx" +#include "bout/utils.hxx" + +#include +#include + +class Options; +class Solver; + +class LaplaceXYpetsc : public LaplaceXY { +public: + LaplaceXYpetsc(Mesh* m = nullptr, Options* opt = nullptr, CELL_LOC loc = CELL_CENTRE); + LaplaceXYpetsc(const LaplaceXYpetsc&) = delete; + LaplaceXYpetsc(LaplaceXYpetsc&&) = delete; + LaplaceXYpetsc& operator=(const LaplaceXYpetsc&) = delete; + LaplaceXYpetsc& operator=(LaplaceXYpetsc&&) = delete; + ~LaplaceXYpetsc() override; + + /*! + * Set coefficients (A, B) in equation: + * Div( A * Grad_perp(x) ) + B*x = b + */ + void setCoefs(const Field2D& A, const Field2D& B) override; + + /*! + * Solve Laplacian in X-Y + * + * Inputs + * ====== + * + * rhs - The field to be inverted. This must be allocated + * and contain valid data. + * x0 - Initial guess at the solution. If this is unallocated + * then an initial guess of zero will be used. + * + * Returns + * ======= + * + * The solution as a Field2D. On failure an exception will be raised + * + */ + Field2D solve(const Field2D& rhs, const Field2D& x0) override; + + /*! + * Preconditioner function + * This is called by PETSc via a static function. + * and should not be called by external users + */ + int precon(Vec x, Vec y); + + /*! + * If this method is called, save some performance monitoring information + */ + void savePerformance(Solver& solver, const std::string& name = ""); + +private: + PetscLib lib; ///< Requires PETSc library + Mat MatA = nullptr; ///< Matrix to be inverted + Vec xs = nullptr; ///< Solution vector + Vec bs = nullptr; ///< RHS vectors + KSP ksp = nullptr; ///< Krylov Subspace solver + PC pc = nullptr; ///< Preconditioner + + Mesh* localmesh; ///< The mesh this operates on, provides metrics and communication + + /// default prefix for writing performance logging variables + std::string default_prefix; + + // Preconditioner + int xstart, xend; + int nloc, nsys; + Matrix acoef, bcoef, ccoef, xvals, bvals; + std::unique_ptr> cr; ///< Tridiagonal solver + + // Use finite volume or finite difference discretization + bool finite_volume{true}; + + // Y derivatives + bool include_y_derivs; // Include Y derivative terms? + + // Boundary conditions + bool x_inner_dirichlet; // Dirichlet on inner X boundary? + bool x_outer_dirichlet{false}; // Dirichlet on outer X boundary? + std::string y_bndry{"neumann"}; // Boundary condition for y-boundary + + // Location of the rhs and solution + CELL_LOC location; + + /*! + * Number of grid points on this processor + */ + int localSize(); + + /*! + * Return the communicator for XY + */ + MPI_Comm communicator(); + + /*! + * Return the global index of a local (x,y) coordinate + * including guard cells. + * Boundary cells have a global index of -1 + * + * To do this, a Field2D (indexXY) is used to store + * the index as a floating point number which is then rounded + * to an integer. Guard cells are filled by communication + * so no additional logic is needed in Mesh. + */ + int globalIndex(int x, int y); + Field2D indexXY; ///< Global index (integer stored as BoutReal) + + // Save performance information? + bool save_performance = false; + + // Running average of number of iterations taken for solve in each output timestep + BoutReal average_iterations = 0.; + + // Variable to store the final result of average_iterations, since output is + // written after all other monitors have been called, and average_iterations + // must be reset in the monitor + BoutReal output_average_iterations = 0.; + + // Running total of number of calls to the solver in each output timestep + int n_calls = 0; + + // Utility methods + void setPreallocationFiniteVolume(PetscInt* d_nnz, PetscInt* o_nnz); + void setPreallocationFiniteDifference(PetscInt* d_nnz, PetscInt* o_nnz); + void setMatrixElementsFiniteVolume(const Field2D& A, const Field2D& B); + void setMatrixElementsFiniteDifference(const Field2D& A, const Field2D& B); + void solveFiniteVolume(const Field2D& x0); + void solveFiniteDifference(const Field2D& x0); + + // Monitor class used to reset performance-monitoring variables for a new + // output timestep + friend class LaplaceXYMonitor; + class LaplaceXYMonitor : public Monitor { + public: + LaplaceXYMonitor(LaplaceXYpetsc& owner) : laplacexy(&owner) {} + + int call(Solver* /*solver*/, BoutReal /*time*/, int /*iter*/, int /*nout*/) override; + void outputVars(Options& output_options, const std::string& time_dimension) override; + + private: + // LaplaceXY object that this monitor belongs to + LaplaceXYpetsc* laplacexy; + }; + + LaplaceXYMonitor monitor; +}; + +namespace { +const inline RegisterLaplaceXY registerlaplacexypetsc{"petsc"}; +} + +#endif // BOUT_HAS_PETSC +#endif // BOUT_LAPLACE_XY_PETSC_H diff --git a/src/invert/laplacexy2/laplacexy2.cxx b/src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.cxx similarity index 90% rename from src/invert/laplacexy2/laplacexy2.cxx rename to src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.cxx index 713fd8e68f..01019466a8 100644 --- a/src/invert/laplacexy2/laplacexy2.cxx +++ b/src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.cxx @@ -2,26 +2,34 @@ #if BOUT_HAS_PETSC and not BOUT_USE_METRIC_3D -#include -#include -#include -#include -#include -#include -#include +#include "laplacexy-petsc2.hxx" + +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutcomm.hxx" +#include "bout/coordinates.hxx" +#include "bout/field2d.hxx" +#include "bout/globalindexer.hxx" +#include "bout/globals.hxx" +#include "bout/operatorstencil.hxx" +#include "bout/options.hxx" +#include "bout/petsc_interface.hxx" +#include "bout/region.hxx" +#include "bout/sys/range.hxx" +#include "bout/sys/timer.hxx" + +#include #include -#include - namespace { Ind2D index2d(Mesh* mesh, int x, int y) { int ny = mesh->LocalNy; - return Ind2D(x * ny + y, ny, 1); + return Ind2D((x * ny) + y, ny, 1); } } // namespace -LaplaceXY2::LaplaceXY2(Mesh* m, Options* opt, const CELL_LOC loc) +LaplaceXYpetsc2::LaplaceXYpetsc2(Mesh* m, Options* opt, const CELL_LOC loc) : localmesh(m == nullptr ? bout::globals::mesh : m), indexConverter(std::make_shared>( localmesh, squareStencil(localmesh))), @@ -100,12 +108,12 @@ LaplaceXY2::LaplaceXY2(Mesh* m, Options* opt, const CELL_LOC loc) // Decide boundary condititions if (localmesh->periodicY(localmesh->xstart)) { // Periodic in Y, so in the core - opt->get("core_bndry_dirichlet", x_inner_dirichlet, false); + x_inner_dirichlet = (*opt)["core_bndry_dirichlet"].withDefault(false); } else { // Non-periodic, so in the PF region - opt->get("pf_bndry_dirichlet", x_inner_dirichlet, true); + x_inner_dirichlet = (*opt)["pf_bndry_dirichlet"].withDefault(true); } - opt->get("y_bndry_dirichlet", y_bndry_dirichlet, false); + y_bndry_dirichlet = (*opt)["y_bndry_dirichlet"].withDefault(false); /////////////////////////////////////////////////// // Including Y derivatives? @@ -120,10 +128,10 @@ LaplaceXY2::LaplaceXY2(Mesh* m, Options* opt, const CELL_LOC loc) Field2D zero(0., localmesh); one.setLocation(location); zero.setLocation(location); - setCoefs(one, zero); + LaplaceXYpetsc2::setCoefs(one, zero); } -void LaplaceXY2::setCoefs(const Field2D& A, const Field2D& B) { +void LaplaceXYpetsc2::setCoefs(const Field2D& A, const Field2D& B) { Timer timer("invert"); ASSERT1(A.getMesh() == localmesh); @@ -290,7 +298,7 @@ void LaplaceXY2::setCoefs(const Field2D& A, const Field2D& B) { #endif } -LaplaceXY2::~LaplaceXY2() { +LaplaceXYpetsc2::~LaplaceXYpetsc2() { PetscBool is_finalised; PetscFinalized(&is_finalised); @@ -300,7 +308,7 @@ LaplaceXY2::~LaplaceXY2() { } } -Field2D LaplaceXY2::solve(const Field2D& rhs, const Field2D& x0) { +Field2D LaplaceXYpetsc2::solve(const Field2D& rhs, const Field2D& x0) { Timer timer("invert"); ASSERT1(rhs.getMesh() == localmesh); @@ -310,7 +318,8 @@ Field2D LaplaceXY2::solve(const Field2D& rhs, const Field2D& x0) { // Load initial guess x0 into xs and rhs into bs - PetscVector xs(x0, indexConverter), bs(rhs, indexConverter); + PetscVector xs(x0, indexConverter); + PetscVector bs(rhs, indexConverter); if (localmesh->firstX()) { if (x_inner_dirichlet) { @@ -381,11 +390,11 @@ Field2D LaplaceXY2::solve(const Field2D& rhs, const Field2D& x0) { // Solve the system KSPSolve(ksp, *bs.get(), *xs.get()); - KSPConvergedReason reason; + KSPConvergedReason reason = KSP_CONVERGED_ITERATING; KSPGetConvergedReason(ksp, &reason); if (reason <= 0) { - throw BoutException("LaplaceXY2 failed to converge. Reason {} ({:d})", + throw BoutException("LaplaceXYpetsc2 failed to converge. Reason {} ({:d})", KSPConvergedReasons[reason], static_cast(reason)); } diff --git a/include/bout/invert/laplacexy2.hxx b/src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.hxx similarity index 62% rename from include/bout/invert/laplacexy2.hxx rename to src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.hxx index fe1ddf50dd..daf09ac709 100644 --- a/include/bout/invert/laplacexy2.hxx +++ b/src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.hxx @@ -32,63 +32,47 @@ * **************************************************************************/ -#ifndef BOUT_LAPLACE_XY2_H -#define BOUT_LAPLACE_XY2_H +#ifndef BOUT_LAPLACE_XY_PETSC2_H +#define BOUT_LAPLACE_XY_PETSC2_H #include "bout/build_defines.hxx" - -#if (not BOUT_HAS_PETSC) or BOUT_USE_METRIC_3D -// If no PETSc or 3D metrics - -#warning LaplaceXY2 requires PETSc and 2D metrics. No LaplaceXY2 available - -#include -#include -#include - -/*! - * Create a dummy class so that code will compile - * without PETSc, but will throw an exception if - * LaplaceXY is used. - */ -class LaplaceXY2 { +#include "bout/invert/laplacexy.hxx" + +#if !BOUT_HAS_PETSC +namespace { +RegisterUnavailableLaplaceXY + registerlaplacexypetsc2("petsc2", "BOUT++ was not configured with PETSc"); +} + +#elif BOUT_USE_METRIC_3D +namespace { +RegisterUnavailableLaplaceXY + registerlaplacexypetsc2("petsc2", "BOUT++ was configured with 3D metrics"); +} + +#else // BOUT_HAS_PETSC + +#include "bout/bout_types.hxx" +#include "bout/field2d.hxx" +#include "bout/globalindexer.hxx" +#include "bout/mesh.hxx" +#include "bout/petsc_interface.hxx" +#include "bout/petsclib.hxx" + +class LaplaceXYpetsc2 : public LaplaceXY { public: - LaplaceXY2(Mesh* UNUSED(m) = nullptr, Options* UNUSED(opt) = nullptr, - const CELL_LOC UNUSED(loc) = CELL_CENTRE) { - throw BoutException( - "LaplaceXY2 requires PETSc and 2D metrics. No LaplaceXY2 available"); - } - void setCoefs(const Field2D& UNUSED(A), const Field2D& UNUSED(B)) {} - Field2D solve(const Field2D& UNUSED(rhs), const Field2D& UNUSED(x0)) { - throw BoutException( - "LaplaceXY2 requires PETSc and 2D metrics. No LaplaceXY2 available"); - } -}; - -#else // BOUT_HAS_PETSC and 2D metrics - -#include "bout/utils.hxx" -#include -#include -#include -#include - -class LaplaceXY2 { -public: - /*! - * Constructor - */ - LaplaceXY2(Mesh* m = nullptr, Options* opt = nullptr, const CELL_LOC loc = CELL_CENTRE); - /*! - * Destructor - */ - ~LaplaceXY2(); + LaplaceXYpetsc2(Mesh* m = nullptr, Options* opt = nullptr, CELL_LOC loc = CELL_CENTRE); + LaplaceXYpetsc2(const LaplaceXYpetsc2&) = default; + LaplaceXYpetsc2(LaplaceXYpetsc2&&) = delete; + LaplaceXYpetsc2& operator=(const LaplaceXYpetsc2&) = default; + LaplaceXYpetsc2& operator=(LaplaceXYpetsc2&&) = delete; + ~LaplaceXYpetsc2() override; /*! * Set coefficients (A, B) in equation: * Div( A * Grad_perp(x) ) + B*x = b */ - void setCoefs(const Field2D& A, const Field2D& B); + void setCoefs(const Field2D& A, const Field2D& B) override; /*! * Solve Laplacian in X-Y @@ -107,7 +91,7 @@ public: * The solution as a Field2D. On failure an exception will be raised * */ - Field2D solve(const Field2D& rhs, const Field2D& x0); + Field2D solve(const Field2D& rhs, const Field2D& x0) override; /*! * Preconditioner function @@ -123,8 +107,8 @@ private: IndexerPtr indexConverter; PetscMatrix matrix; ///< Matrix to be inverted - KSP ksp; ///< Krylov Subspace solver - PC pc; ///< Preconditioner + KSP ksp = nullptr; ///< Krylov Subspace solver + PC pc = nullptr; ///< Preconditioner // Y derivatives bool include_y_derivs; // Include Y derivative terms? @@ -142,5 +126,9 @@ private: MPI_Comm communicator(); }; +namespace { +const inline RegisterLaplaceXY registerlaplacexypetsc2{"petsc2"}; +} + #endif // BOUT_HAS_PETSC -#endif // BOUT_LAPLACE_XY2_H +#endif // BOUT_LAPLACE_XY_PETSC2_H diff --git a/src/invert/laplacexy/laplacexy.cxx b/src/invert/laplacexy/laplacexy.cxx index bdd22e29bd..3bac2d9d2b 100644 --- a/src/invert/laplacexy/laplacexy.cxx +++ b/src/invert/laplacexy/laplacexy.cxx @@ -1,1889 +1,10 @@ -#include "bout/build_defines.hxx" - -#if BOUT_HAS_PETSC +// NOLINTBEGIN(misc-include-cleaner, unused-includes) +#include "impls/hypre/laplacexy-hypre.hxx" +#include "impls/petsc/laplacexy-petsc.hxx" +#include "impls/petsc2/laplacexy-petsc2.hxx" +// NOLINTEND(misc-include-cleaner, unused-includes) #include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -static PetscErrorCode laplacePCapply(PC pc, Vec x, Vec y) { - int ierr; - - // Get the context - LaplaceXY* s; - ierr = PCShellGetContext(pc, reinterpret_cast(&s)); - CHKERRQ(ierr); - - PetscFunctionReturn(s->precon(x, y)); -} - -LaplaceXY::LaplaceXY(Mesh* m, Options* opt, const CELL_LOC loc) - : lib(opt == nullptr ? &(Options::root()["laplacexy"]) : opt), - localmesh(m == nullptr ? bout::globals::mesh : m), location(loc), monitor(*this) { - Timer timer("invert"); - - if (opt == nullptr) { - // If no options supplied, use default - opt = &(Options::root()["laplacexy"]); - } - - finite_volume = - (*opt)["finite_volume"] - .doc("Use finite volume rather than finite difference discretisation.") - .withDefault(true); - - /////////////////////////////////////////////////// - // Boundary condititions options - if (localmesh->periodicY(localmesh->xstart)) { - // Periodic in Y, so in the core - x_inner_dirichlet = (*opt)["core_bndry_dirichlet"].withDefault(false); - } else { - // Non-periodic, so in the PF region - x_inner_dirichlet = (*opt)["pf_bndry_dirichlet"].withDefault(true); - } - if ((*opt)["y_bndry_dirichlet"].isSet()) { - throw BoutException("y_bndry_dirichlet has been deprecated. Use y_bndry=dirichlet " - "instead."); - } else { - y_bndry = (*opt)["y_bndry"].withDefault("neumann"); - } - - // Check value of y_bndry is a supported option - if (not(y_bndry == "dirichlet" or y_bndry == "neumann" or y_bndry == "free_o3")) { - - throw BoutException("Unrecognized option '{}' for laplacexy:ybndry", y_bndry); - } - - if (not finite_volume) { - // Check we can use corner cells - if (not localmesh->include_corner_cells) { - throw BoutException( - "Finite difference form of LaplaceXY allows non-orthogonal x- and " - "y-directions, so requires mesh:include_corner_cells=true."); - } - } - - // Use name of options section as the default prefix for performance logging variables - default_prefix = opt->name(); - - // Get MPI communicator - auto comm = BoutComm::get(); - - // Local size - const int localN = localSize(); - - // Create Vectors - VecCreate(comm, &xs); - VecSetSizes(xs, localN, PETSC_DETERMINE); - VecSetFromOptions(xs); - VecDuplicate(xs, &bs); - - // Set size of Matrix on each processor to localN x localN - MatCreate(comm, &MatA); - MatSetSizes(MatA, localN, localN, PETSC_DETERMINE, PETSC_DETERMINE); - MatSetFromOptions(MatA); - - ////////////////////////////////////////////////// - // Specify local indices. This creates a mapping - // from local indices to index, using a Field2D object - - indexXY = -1; // Set all points to -1, indicating out of domain - - int ind = 0; - - // Y boundaries - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - indexXY(it.ind, localmesh->ystart - 1) = ind++; - } - if ((not finite_volume) and localmesh->hasBndryLowerY()) { - // Corner boundary cells - if (localmesh->firstX()) { - indexXY(localmesh->xstart - 1, localmesh->ystart - 1) = ind++; - } - if (localmesh->lastX()) { - indexXY(localmesh->xend + 1, localmesh->ystart - 1) = ind++; - } - } - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - indexXY(it.ind, localmesh->yend + 1) = ind++; - } - if ((not finite_volume) and localmesh->hasBndryUpperY()) { - // Corner boundary cells - if (localmesh->firstX()) { - indexXY(localmesh->xstart - 1, localmesh->yend + 1) = ind++; - } - if (localmesh->lastX()) { - indexXY(localmesh->xend + 1, localmesh->yend + 1) = ind++; - } - } - - xstart = localmesh->xstart; - if (localmesh->firstX()) { - xstart -= 1; // Include X guard cells - } - xend = localmesh->xend; - if (localmesh->lastX()) { - xend += 1; - } - for (int x = xstart; x <= xend; x++) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - indexXY(x, y) = ind++; - } - } - - ASSERT1(ind == localN); // Reached end of range - - ////////////////////////////////////////////////// - // Allocate storage for preconditioner - - nloc = xend - xstart + 1; // Number of X points on this processor - nsys = localmesh->yend - localmesh->ystart + 1; // Number of separate Y slices - - acoef.reallocate(nsys, nloc); - bcoef.reallocate(nsys, nloc); - ccoef.reallocate(nsys, nloc); - xvals.reallocate(nsys, nloc); - bvals.reallocate(nsys, nloc); - - // Create a cyclic reduction object - cr = bout::utils::make_unique>(localmesh->getXcomm(), nloc); - - ////////////////////////////////////////////////// - // Pre-allocate PETSc storage - - PetscInt *d_nnz, *o_nnz; - PetscMalloc((localN) * sizeof(PetscInt), &d_nnz); - PetscMalloc((localN) * sizeof(PetscInt), &o_nnz); - - if (finite_volume) { - setPreallocationFiniteVolume(d_nnz, o_nnz); - } else { - setPreallocationFiniteDifference(d_nnz, o_nnz); - } - // Pre-allocate - MatMPIAIJSetPreallocation(MatA, 0, d_nnz, 0, o_nnz); - MatSetUp(MatA); - - PetscFree(d_nnz); - PetscFree(o_nnz); - - // Determine which row/columns of the matrix are locally owned - int Istart, Iend; - MatGetOwnershipRange(MatA, &Istart, &Iend); - - // Convert indexXY from local index to global index - indexXY += Istart; - - // Now communicate to fill guard cells - // Note, this includes corner cells if necessary - localmesh->communicate(indexXY); - - ////////////////////////////////////////////////// - // Set up KSP - - // Declare KSP Context - KSPCreate(comm, &ksp); - - // Configure Linear Solver - - const bool direct = (*opt)["direct"].doc("Use a direct LU solver").withDefault(false); - - if (direct) { - KSPGetPC(ksp, &pc); - PCSetType(pc, PCLU); -#if PETSC_VERSION_GE(3, 9, 0) - PCFactorSetMatSolverType(pc, "mumps"); -#else - PCFactorSetMatSolverPackage(pc, "mumps"); -#endif - } else { - - // Convergence Parameters. Solution is considered converged if |r_k| < max( rtol * |b| , atol ) - // where r_k = b - Ax_k. The solution is considered diverged if |r_k| > dtol * |b|. - - const BoutReal rtol = (*opt)["rtol"].doc("Relative tolerance").withDefault(1e-5); - const BoutReal atol = - (*opt)["atol"] - .doc("Absolute tolerance. The solution is considered converged if |Ax-b| " - "< max( rtol * |b| , atol )") - .withDefault(1e-10); - const BoutReal dtol = - (*opt)["dtol"] - .doc("The solution is considered diverged if |Ax-b| > dtol * |b|") - .withDefault(1e3); - const int maxits = (*opt)["maxits"].doc("Maximum iterations").withDefault(100000); - - // Get KSP Solver Type - const std::string ksptype = - (*opt)["ksptype"].doc("KSP solver type").withDefault("gmres"); - - // Get PC type - const std::string pctype = - (*opt)["pctype"].doc("Preconditioner type").withDefault("none"); - - KSPSetType(ksp, ksptype.c_str()); - KSPSetTolerances(ksp, rtol, atol, dtol, maxits); - - KSPSetInitialGuessNonzero(ksp, static_cast(true)); - - KSPGetPC(ksp, &pc); - PCSetType(pc, pctype.c_str()); - - if (pctype == "shell") { - // Using tridiagonal solver as preconditioner - PCShellSetApply(pc, laplacePCapply); - PCShellSetContext(pc, this); - - const bool rightprec = - (*opt)["rightprec"].doc("Use right preconditioning?").withDefault(true); - if (rightprec) { - KSPSetPCSide(ksp, PC_RIGHT); // Right preconditioning - } else { - KSPSetPCSide(ksp, PC_LEFT); // Left preconditioning - } - } - } - - lib.setOptionsFromInputFile(ksp); - - /////////////////////////////////////////////////// - // Including Y derivatives? - - include_y_derivs = (*opt)["include_y_derivs"] - .doc("Include Y derivatives in operator to invert?") - .withDefault(true); - - /////////////////////////////////////////////////// - // Set the default coefficients - Field2D one(1., localmesh); - Field2D zero(0., localmesh); - one.setLocation(location); - zero.setLocation(location); - setCoefs(one, zero); -} - -void LaplaceXY::setPreallocationFiniteVolume(PetscInt* d_nnz, PetscInt* o_nnz) { - const int localN = localSize(); - - // This discretisation uses a 5-point stencil - for (int i = 0; i < localN; i++) { - // Non-zero elements on this processor - d_nnz[i] = 5; // Star pattern in 2D - // Non-zero elements on neighboring processor - o_nnz[i] = 0; - } - - // X boundaries - if (localmesh->firstX()) { - // Lower X boundary - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int localIndex = globalIndex(localmesh->xstart - 1, y); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - - d_nnz[localIndex] = 2; // Diagonal sub-matrix - o_nnz[localIndex] = 0; // Off-diagonal sub-matrix - } - } else { - // On another processor - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int localIndex = globalIndex(localmesh->xstart, y); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] -= 1; - o_nnz[localIndex] += 1; - } - } - if (localmesh->lastX()) { - // Upper X boundary - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int localIndex = globalIndex(localmesh->xend + 1, y); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = 2; // Diagonal sub-matrix - o_nnz[localIndex] = 0; // Off-diagonal sub-matrix - } - } else { - // On another processor - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int localIndex = globalIndex(localmesh->xend, y); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] -= 1; - o_nnz[localIndex] += 1; - } - } - // Y boundaries - - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - // Default to no boundary - // NOTE: This assumes that communications in Y are to other - // processors. If Y is communicated with this processor (e.g. NYPE=1) - // then this will result in PETSc warnings about out of range allocations - { - const int localIndex = globalIndex(x, localmesh->ystart); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - // d_nnz[localIndex] -= 1; // Note: Slightly inefficient - o_nnz[localIndex] += 1; - } - { - const int localIndex = globalIndex(x, localmesh->yend); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - // d_nnz[localIndex] -= 1; // Note: Slightly inefficient - o_nnz[localIndex] += 1; - } - } - - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - { - const int localIndex = globalIndex(it.ind, localmesh->ystart - 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = 2; // Diagonal sub-matrix - o_nnz[localIndex] = 0; // Off-diagonal sub-matrix - } - { - const int localIndex = globalIndex(it.ind, localmesh->ystart); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] += 1; - o_nnz[localIndex] -= 1; - } - } - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - { - const int localIndex = globalIndex(it.ind, localmesh->yend + 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = 2; // Diagonal sub-matrix - o_nnz[localIndex] = 0; // Off-diagonal sub-matrix - } - { - const int localIndex = globalIndex(it.ind, localmesh->yend); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] += 1; - o_nnz[localIndex] -= 1; - } - } -} - -void LaplaceXY::setPreallocationFiniteDifference(PetscInt* d_nnz, PetscInt* o_nnz) { - const int localN = localSize(); - - // This discretisation uses a 9-point stencil - for (int i = 0; i < localN; i++) { - // Non-zero elements on this processor - d_nnz[i] = 9; // Square pattern in 2D - // Non-zero elements on neighboring processor - o_nnz[i] = 0; - } - - // X boundaries - if (localmesh->firstX()) { - // Lower X boundary - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int localIndex = globalIndex(localmesh->xstart - 1, y); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - - d_nnz[localIndex] = 2; // Diagonal sub-matrix - o_nnz[localIndex] = 0; // Off-diagonal sub-matrix - } - } else { - // On another processor - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int localIndex = globalIndex(localmesh->xstart, y); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] -= 3; - o_nnz[localIndex] += 3; - } - } - if (localmesh->lastX()) { - // Upper X boundary - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int localIndex = globalIndex(localmesh->xend + 1, y); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = 2; // Diagonal sub-matrix - o_nnz[localIndex] = 0; // Off-diagonal sub-matrix - } - } else { - // On another processor - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int localIndex = globalIndex(localmesh->xend, y); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] -= 3; - o_nnz[localIndex] += 3; - } - } - // Y boundaries - const int y_bndry_stencil_size = (y_bndry == "free_o3") ? 4 : 2; - - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - // Default to no boundary - // NOTE: This assumes that communications in Y are to other - // processors. If Y is communicated with this processor (e.g. NYPE=1) - // then this will result in PETSc warnings about out of range allocations - { - const int localIndex = globalIndex(x, localmesh->ystart); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - // d_nnz[localIndex] -= 3; // Note: Slightly inefficient - o_nnz[localIndex] += 3; - } - { - const int localIndex = globalIndex(x, localmesh->yend); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - // d_nnz[localIndex] -= 3; // Note: Slightly inefficient - o_nnz[localIndex] += 3; - } - } - - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, they are handled specially below - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - { - const int localIndex = globalIndex(it.ind, localmesh->ystart - 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = y_bndry_stencil_size; // Diagonal sub-matrix - o_nnz[localIndex] = 0; // Off-diagonal sub-matrix - } - { - const int localIndex = globalIndex(it.ind, localmesh->ystart); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - //d_nnz[localIndex] += 3; - o_nnz[localIndex] -= 3; - } - } - if (localmesh->hasBndryLowerY()) { - if (y_bndry == "dirichlet") { - // special handling for the corners, since we use a free_o3 y-boundary - // condition just in the corners when y_bndry=="dirichlet" - if (localmesh->firstX()) { - const int localIndex = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = 4; - } - if (localmesh->lastX()) { - const int localIndex = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = 4; - } - } else { - if (localmesh->firstX()) { - const int localIndex = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = y_bndry_stencil_size; - } - if (localmesh->lastX()) { - const int localIndex = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = y_bndry_stencil_size; - } - } - } - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, they are handled specially below - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - { - const int localIndex = globalIndex(it.ind, localmesh->yend + 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = y_bndry_stencil_size; // Diagonal sub-matrix - o_nnz[localIndex] = 0; // Off-diagonal sub-matrix - } - { - const int localIndex = globalIndex(it.ind, localmesh->yend); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - //d_nnz[localIndex] += 3; - o_nnz[localIndex] -= 3; - } - } - if (localmesh->hasBndryUpperY()) { - if (y_bndry == "dirichlet") { - // special handling for the corners, since we use a free_o3 y-boundary - // condition just in the corners when y_bndry=="dirichlet" - if (localmesh->firstX()) { - const int localIndex = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = 4; - } - if (localmesh->lastX()) { - const int localIndex = globalIndex(localmesh->xend + 1, localmesh->yend + 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = 4; - } - } else { - if (localmesh->firstX()) { - const int localIndex = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = y_bndry_stencil_size; - } - if (localmesh->lastX()) { - const int localIndex = globalIndex(localmesh->xend + 1, localmesh->yend + 1); - ASSERT1((localIndex >= 0) && (localIndex < localN)); - d_nnz[localIndex] = y_bndry_stencil_size; - } - } - } -} - -void LaplaceXY::setCoefs(const Field2D& A, const Field2D& B) { - Timer timer("invert"); - - ASSERT1(A.getMesh() == localmesh); - ASSERT1(B.getMesh() == localmesh); - ASSERT1(A.getLocation() == location); - ASSERT1(B.getLocation() == location); - - if (finite_volume) { - setMatrixElementsFiniteVolume(A, B); - } else { - setMatrixElementsFiniteDifference(A, B); - } - - // X boundaries - if (localmesh->firstX()) { - if (x_inner_dirichlet) { - - // Dirichlet on inner X boundary - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int row = globalIndex(localmesh->xstart - 1, y); - PetscScalar val = 0.5; - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - int col = globalIndex(localmesh->xstart, y); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - // Preconditioner - bcoef(y - localmesh->ystart, 0) = 0.5; - ccoef(y - localmesh->ystart, 0) = 0.5; - } - - } else { - - // Neumann on inner X boundary - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int row = globalIndex(localmesh->xstart - 1, y); - PetscScalar val = 1.0; - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - int col = globalIndex(localmesh->xstart, y); - val = -1.0; - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - // Preconditioner - bcoef(y - localmesh->ystart, 0) = 1.0; - ccoef(y - localmesh->ystart, 0) = -1.0; - } - } - } - if (localmesh->lastX()) { - // Dirichlet on outer X boundary - - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int row = globalIndex(localmesh->xend + 1, y); - PetscScalar val = 0.5; - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - int col = globalIndex(localmesh->xend, y); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - // Preconditioner - acoef(y - localmesh->ystart, localmesh->xend + 1 - xstart) = 0.5; - bcoef(y - localmesh->ystart, localmesh->xend + 1 - xstart) = 0.5; - } - } - - if (y_bndry == "dirichlet") { - // Dirichlet on Y boundaries - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int row = globalIndex(it.ind, localmesh->ystart - 1); - PetscScalar val = 0.5; - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - int col = globalIndex(it.ind, localmesh->ystart); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } - - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int row = globalIndex(it.ind, localmesh->yend + 1); - PetscScalar val = 0.5; - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - int col = globalIndex(it.ind, localmesh->yend); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } - } else if (y_bndry == "neumann") { - // Neumann on Y boundaries - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int row = globalIndex(it.ind, localmesh->ystart - 1); - PetscScalar val = 1.0; - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -1.0; - int col = globalIndex(it.ind, localmesh->ystart); - - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } - - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int row = globalIndex(it.ind, localmesh->yend + 1); - PetscScalar val = 1.0; - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -1.0; - int col = globalIndex(it.ind, localmesh->yend); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } - } else if (y_bndry == "free_o3") { - // 'free_o3' extrapolating boundary condition on Y boundaries - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int row = globalIndex(it.ind, localmesh->ystart - 1); - PetscScalar val = 1.0; - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -3.0; - int col = globalIndex(it.ind, localmesh->ystart); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = 3.0; - col = globalIndex(it.ind, localmesh->ystart + 1); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = -1.0; - col = globalIndex(it.ind, localmesh->ystart + 2); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } - - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int row = globalIndex(it.ind, localmesh->yend + 1); - PetscScalar val = 1.0; - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -3.0; - int col = globalIndex(it.ind, localmesh->yend); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = 3.0; - col = globalIndex(it.ind, localmesh->yend - 1); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = -1.0; - col = globalIndex(it.ind, localmesh->yend - 2); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } - } else { - throw BoutException("Unsupported option for y_bndry"); - } - - if (not finite_volume) { - // Handle corner boundary cells in case we need to include D2DXDY - // Apply the y-boundary-condition to the cells in the x-boundary - this is an - // arbitrary choice, cf. connections around the X-point - - if (localmesh->hasBndryLowerY()) { - if (localmesh->firstX()) { - if (y_bndry == "neumann") { - // Neumann y-bc - // f(xs-1,ys-1) = f(xs-1,ys) - PetscScalar val = 1.0; - int row = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -1.0; - int col = globalIndex(localmesh->xstart - 1, localmesh->ystart); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } else if (y_bndry == "free_o3" or y_bndry == "dirichlet") { - // 'free_o3' extrapolating boundary condition on Y boundaries - // f(xs-1,ys-1) = 3*f(xs-1,ys) - 3*f(xs-1,ys+1) + f(xs-1,ys+2) - // - // Use free_o3 at the corners for Dirichlet y-boundaries because we don't know - // what value to pass for the corner - PetscScalar val = 1.0; - int row = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -3.0; - int col = globalIndex(localmesh->xstart - 1, localmesh->ystart); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = 3.0; - col = globalIndex(localmesh->xstart - 1, localmesh->ystart + 1); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = -1.0; - col = globalIndex(localmesh->xstart - 1, localmesh->ystart + 2); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } else { - throw BoutException("Unsupported option for y_bndry"); - } - } - if (localmesh->lastX()) { - if (y_bndry == "neumann") { - // Neumann y-bc - // f(xe+1,ys-1) = f(xe+1,ys) - PetscScalar val = 1.0; - int row = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -1.0; - int col = globalIndex(localmesh->xend + 1, localmesh->ystart); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } else if (y_bndry == "free_o3" or y_bndry == "dirichlet") { - // 'free_o3' extrapolating boundary condition on Y boundaries - // f(xe+1,ys-1) = 3*f(xe+1,ys) - 3*f(xe+1,ys+1) + f(xe+1,ys+2) - // - // Use free_o3 at the corners for Dirichlet y-boundaries because we don't know - // what value to pass for the corner - PetscScalar val = 1.0; - int row = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -3.0; - int col = globalIndex(localmesh->xend + 1, localmesh->ystart); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = 3.0; - col = globalIndex(localmesh->xend + 1, localmesh->ystart + 1); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = -1.0; - col = globalIndex(localmesh->xend + 1, localmesh->ystart + 2); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } else { - throw BoutException("Unsupported option for y_bndry"); - } - } - } - if (localmesh->hasBndryUpperY()) { - if (localmesh->firstX()) { - if (y_bndry == "neumann") { - // Neumann y-bc - // f(xs-1,ys-1) = f(xs-1,ys) - PetscScalar val = 1.0; - int row = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -1.0; - int col = globalIndex(localmesh->xstart - 1, localmesh->yend); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } else if (y_bndry == "free_o3" or y_bndry == "dirichlet") { - // 'free_o3' extrapolating boundary condition on Y boundaries - // f(xs-1,ys-1) = 3*f(xs-1,ys) - 3*f(xs-1,ys+1) + f(xs-1,ys+2) - // - // Use free_o3 at the corners for Dirichlet y-boundaries because we don't know - // what value to pass for the corner - PetscScalar val = 1.0; - int row = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -3.0; - int col = globalIndex(localmesh->xstart - 1, localmesh->yend); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = 3.0; - col = globalIndex(localmesh->xstart - 1, localmesh->yend - 1); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = -1.0; - col = globalIndex(localmesh->xstart - 1, localmesh->yend - 2); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } else { - throw BoutException("Unsupported option for y_bndry"); - } - } - if (localmesh->lastX()) { - if (y_bndry == "neumann") { - // Neumann y-bc - // f(xe+1,ys-1) = f(xe+1,ys) - PetscScalar val = 1.0; - int row = globalIndex(localmesh->xend + 1, localmesh->yend + 1); - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -1.0; - int col = globalIndex(localmesh->xend + 1, localmesh->yend); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } else if (y_bndry == "free_o3" or y_bndry == "dirichlet") { - // 'free_o3' extrapolating boundary condition on Y boundaries - // f(xe+1,ys-1) = 3*f(xe+1,ys) - 3*f(xe+1,ys+1) + f(xe+1,ys+2) - // - // Use free_o3 at the corners for Dirichlet y-boundaries because we don't know - // what value to pass for the corner - PetscScalar val = 1.0; - int row = globalIndex(localmesh->xend + 1, localmesh->yend + 1); - MatSetValues(MatA, 1, &row, 1, &row, &val, INSERT_VALUES); - - val = -3.0; - int col = globalIndex(localmesh->xend + 1, localmesh->yend); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = 3.0; - col = globalIndex(localmesh->xend + 1, localmesh->yend - 1); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - - val = -1.0; - col = globalIndex(localmesh->xend + 1, localmesh->yend - 2); - MatSetValues(MatA, 1, &row, 1, &col, &val, INSERT_VALUES); - } else { - throw BoutException("Unsupported option for y_bndry"); - } - } - } - } - - // Assemble Matrix - MatAssemblyBegin(MatA, MAT_FINAL_ASSEMBLY); - MatAssemblyEnd(MatA, MAT_FINAL_ASSEMBLY); - - // Set the operator -#if PETSC_VERSION_GE(3, 5, 0) - KSPSetOperators(ksp, MatA, MatA); -#else - KSPSetOperators(ksp, MatA, MatA, DIFFERENT_NONZERO_PATTERN); -#endif - - // Set coefficients for preconditioner - cr->setCoefs(acoef, bcoef, ccoef); -} - -void LaplaceXY::setMatrixElementsFiniteVolume(const Field2D& A, const Field2D& B) { - ////////////////////////////////////////////////// - // Set Matrix elements - // - // (1/J) d/dx ( J * g11 d/dx ) + (1/J) d/dy ( J * g22 d/dy ) - - auto coords = localmesh->getCoordinates(location); - const Field2D J_DC = DC(coords->J); - const Field2D g11_DC = DC(coords->g11); - const Field2D dx_DC = DC(coords->dx); - const Field2D dy_DC = DC(coords->dy); - const Field2D g_22_DC = DC(coords->g_22); - const Field2D g_23_DC = DC(coords->g_23); - const Field2D g23_DC = DC(coords->g23); - - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - // stencil entries - PetscScalar c, xm, xp, ym, yp; - - // XX component - - // Metrics on x+1/2 boundary - BoutReal J = 0.5 * (J_DC(x, y) + J_DC(x + 1, y)); - BoutReal g11 = 0.5 * (g11_DC(x, y) + g11_DC(x + 1, y)); - BoutReal dx = 0.5 * (dx_DC(x, y) + dx_DC(x + 1, y)); - BoutReal Acoef = 0.5 * (A(x, y) + A(x + 1, y)); - - BoutReal val = Acoef * J * g11 / (J_DC(x, y) * dx * dx_DC(x, y)); - xp = val; - c = -val; - - // Metrics on x-1/2 boundary - J = 0.5 * (J_DC(x, y) + J_DC(x - 1, y)); - g11 = 0.5 * (g11_DC(x, y) + g11_DC(x - 1, y)); - dx = 0.5 * (dx_DC(x, y) + dx_DC(x - 1, y)); - Acoef = 0.5 * (A(x, y) + A(x - 1, y)); - - val = Acoef * J * g11 / (J_DC(x, y) * dx * dx_DC(x, y)); - xm = val; - c -= val; - - c += B(x, y); - - // Put values into the preconditioner, X derivatives only - acoef(y - localmesh->ystart, x - xstart) = xm; - bcoef(y - localmesh->ystart, x - xstart) = c; - ccoef(y - localmesh->ystart, x - xstart) = xp; - - if (include_y_derivs) { - // YY component - // Metrics at y+1/2 - J = 0.5 * (J_DC(x, y) + J_DC(x, y + 1)); - BoutReal g_22 = 0.5 * (g_22_DC(x, y) + g_22_DC(x, y + 1)); - BoutReal g23 = 0.5 * (g23_DC(x, y) + g23_DC(x, y + 1)); - BoutReal g_23 = 0.5 * (g_23_DC(x, y) + g_23_DC(x, y + 1)); - BoutReal dy = 0.5 * (dy_DC(x, y) + dy_DC(x, y + 1)); - Acoef = 0.5 * (A(x, y + 1) + A(x, y)); - - val = -Acoef * J * g23 * g_23 / (g_22 * J_DC(x, y) * dy * dy_DC(x, y)); - yp = val; - c -= val; - - // Metrics at y-1/2 - J = 0.5 * (J_DC(x, y) + J_DC(x, y - 1)); - g_22 = 0.5 * (g_22_DC(x, y) + g_22_DC(x, y - 1)); - g23 = 0.5 * (g23_DC(x, y) + g23_DC(x, y - 1)); - g_23 = 0.5 * (g_23_DC(x, y) + g_23_DC(x, y - 1)); - dy = 0.5 * (dy_DC(x, y) + dy_DC(x, y - 1)); - Acoef = 0.5 * (A(x, y - 1) + A(x, y)); - - val = -Acoef * J * g23 * g_23 / (g_22 * J_DC(x, y) * dy * dy_DC(x, y)); - ym = val; - c -= val; - } - - ///////////////////////////////////////////////// - // Now have a 5-point stencil for the Laplacian - - int row = globalIndex(x, y); - - // Set the centre (diagonal) - MatSetValues(MatA, 1, &row, 1, &row, &c, INSERT_VALUES); - - // X + 1 - int col = globalIndex(x + 1, y); - MatSetValues(MatA, 1, &row, 1, &col, &xp, INSERT_VALUES); - - // X - 1 - col = globalIndex(x - 1, y); - MatSetValues(MatA, 1, &row, 1, &col, &xm, INSERT_VALUES); - - if (include_y_derivs) { - // Y + 1 - col = globalIndex(x, y + 1); - MatSetValues(MatA, 1, &row, 1, &col, &yp, INSERT_VALUES); - - // Y - 1 - col = globalIndex(x, y - 1); - MatSetValues(MatA, 1, &row, 1, &col, &ym, INSERT_VALUES); - } - } - } -} - -void LaplaceXY::setMatrixElementsFiniteDifference(const Field2D& A, const Field2D& B) { - ////////////////////////////////////////////////// - // Set Matrix elements - // - // Div(A Grad(f)) + B f - // = A Laplace_perp(f) + Grad_perp(A).Grad_perp(f) + B f - // = A*(G1*dfdx + (G2-1/J*d/dy(J/g_22))*dfdy - // + g11*d2fdx2 + (g22-1/g_22)*d2fdy2 + 2*g12*d2fdxdy) - // + g11*dAdx*dfdx + (g22-1/g_22)*dAdy*dfdy + g12*(dAdx*dfdy + dAdy*dfdx) - // + B*f - - auto coords = localmesh->getCoordinates(location); - const Field2D G1_2D = DC(coords->G1); - const Field2D G2_2D = DC(coords->G2); - const Field2D J_2D = DC(coords->J); - const Field2D g11_2D = DC(coords->g11); - const Field2D g_22_2D = DC(coords->g_22); - const Field2D g22_2D = DC(coords->g22); - const Field2D g12_2D = DC(coords->g12); - const Field2D d1_dx_2D = DC(coords->d1_dx); - const Field2D d1_dy_2D = DC(coords->d1_dy); - const Field2D dx_2D = DC(coords->dx); - const Field2D dy_2D = DC(coords->dy); - - const Field2D coef_dfdy = G2_2D - DC(DDY(J_2D / g_22_2D) / J_2D); - - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - // stencil entries - PetscScalar c, xm, xp, ym, yp, xpyp, xpym, xmyp, xmym; - - BoutReal dx = dx_2D(x, y); - - // A*G1*dfdx - BoutReal val = A(x, y) * G1_2D(x, y) / (2. * dx); - xp = val; - xm = -val; - - // A*g11*d2fdx2 - val = A(x, y) * g11_2D(x, y) / SQ(dx); - xp += val; - c = -2. * val; - xm += val; - // Non-uniform grid correction - val = A(x, y) * g11_2D(x, y) * d1_dx_2D(x, y) / (2. * dx); - xp += val; - xm -= val; - - // g11*dAdx*dfdx - val = g11_2D(x, y) * (A(x + 1, y) - A(x - 1, y)) / (4. * SQ(dx)); - xp += val; - xm -= val; - - // B*f - c += B(x, y); - - // Put values into the preconditioner, X derivatives only - acoef(y - localmesh->ystart, x - xstart) = xm; - bcoef(y - localmesh->ystart, x - xstart) = c; - ccoef(y - localmesh->ystart, x - xstart) = xp; - - if (include_y_derivs) { - BoutReal dy = dy_2D(x, y); - BoutReal dAdx = (A(x + 1, y) - A(x - 1, y)) / (2. * dx); - BoutReal dAdy = (A(x, y + 1) - A(x, y - 1)) / (2. * dy); - - // A*(G2-1/J*d/dy(J/g_22))*dfdy - val = A(x, y) * coef_dfdy(x, y) / (2. * dy); - yp = val; - ym = -val; - - // A*(g22-1/g_22)*d2fdy2 - val = A(x, y) * (g22_2D(x, y) - 1. / g_22_2D(x, y)) / SQ(dy); - yp += val; - c -= 2. * val; - ym += val; - // Non-uniform mesh correction - val = A(x, y) * (g22_2D(x, y) - 1. / g_22_2D(x, y)) * d1_dy_2D(x, y) / (2. * dy); - yp += val; - ym -= val; - - // 2*A*g12*d2dfdxdy - val = A(x, y) * g12_2D(x, y) / (2. * dx * dy); - xpyp = val; - xpym = -val; - xmyp = -val; - xmym = val; - - // g22*dAdy*dfdy - val = (g22_2D(x, y) - 1. / g_22_2D(x, y)) * dAdy / (2. * dy); - yp += val; - ym -= val; - - // g12*(dAdx*dfdy + dAdy*dfdx) - val = g12_2D(x, y) * dAdx / (2. * dy); - yp += val; - ym -= val; - val = g12_2D(x, y) * dAdy / (2. * dx); - xp += val; - xm -= val; - } - - ///////////////////////////////////////////////// - // Now have a 9-point stencil for the Laplacian - - int row = globalIndex(x, y); - - // Set the centre (diagonal) - MatSetValues(MatA, 1, &row, 1, &row, &c, INSERT_VALUES); - - // X + 1 - int col = globalIndex(x + 1, y); - MatSetValues(MatA, 1, &row, 1, &col, &xp, INSERT_VALUES); - - // X - 1 - col = globalIndex(x - 1, y); - MatSetValues(MatA, 1, &row, 1, &col, &xm, INSERT_VALUES); - - if (include_y_derivs) { - // Y + 1 - col = globalIndex(x, y + 1); - MatSetValues(MatA, 1, &row, 1, &col, &yp, INSERT_VALUES); - - // Y - 1 - col = globalIndex(x, y - 1); - MatSetValues(MatA, 1, &row, 1, &col, &ym, INSERT_VALUES); - - // X + 1, Y + 1 - col = globalIndex(x + 1, y + 1); - MatSetValues(MatA, 1, &row, 1, &col, &xpyp, INSERT_VALUES); - - // X + 1, Y - 1 - col = globalIndex(x + 1, y - 1); - MatSetValues(MatA, 1, &row, 1, &col, &xpym, INSERT_VALUES); - - // X - 1, Y + 1 - col = globalIndex(x - 1, y + 1); - MatSetValues(MatA, 1, &row, 1, &col, &xmyp, INSERT_VALUES); - - // X - 1, Y - 1 - col = globalIndex(x - 1, y - 1); - MatSetValues(MatA, 1, &row, 1, &col, &xmym, INSERT_VALUES); - } - } - } -} - -LaplaceXY::~LaplaceXY() { - PetscBool is_finalised; - PetscFinalized(&is_finalised); - - if (!is_finalised) { - // PetscFinalize may already have destroyed this object - KSPDestroy(&ksp); - } - - VecDestroy(&xs); - VecDestroy(&bs); - MatDestroy(&MatA); -} - -const Field2D LaplaceXY::solve(const Field2D& rhs, const Field2D& x0) { - Timer timer("invert"); - - ASSERT1(rhs.getMesh() == localmesh); - ASSERT1(x0.getMesh() == localmesh); - ASSERT1(rhs.getLocation() == location); - ASSERT1(x0.getLocation() == location); - - // Load initial guess x0 into xs and rhs into bs - - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(x, y); - - PetscScalar val = x0(x, y); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - val = rhs(x, y); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - - if (finite_volume) { - solveFiniteVolume(x0); - } else { - solveFiniteDifference(x0); - } - - // Assemble RHS Vector - VecAssemblyBegin(bs); - VecAssemblyEnd(bs); - - // Assemble Trial Solution Vector - VecAssemblyBegin(xs); - VecAssemblyEnd(xs); - - // Solve the system - KSPSolve(ksp, bs, xs); - - KSPConvergedReason reason; - KSPGetConvergedReason(ksp, &reason); - - if (reason <= 0) { - throw BoutException("LaplaceXY failed to converge. Reason {} ({:d})", - KSPConvergedReasons[reason], static_cast(reason)); - } - - if (save_performance) { - // Update performance monitoring information - n_calls++; - - int iterations = 0; - KSPGetIterationNumber(ksp, &iterations); - - average_iterations = BoutReal(n_calls - 1) / BoutReal(n_calls) * average_iterations - + BoutReal(iterations) / BoutReal(n_calls); - } - - ////////////////////////// - // Copy data into result - - Field2D result; - result.allocate(); - result.setLocation(location); - - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(x, y); - - PetscScalar val; - VecGetValues(xs, 1, &ind, &val); - result(x, y) = val; - } - } - - // Inner X boundary - if (localmesh->firstX()) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(localmesh->xstart - 1, y); - PetscScalar val; - VecGetValues(xs, 1, &ind, &val); - for (int x = localmesh->xstart - 1; x >= 0; x--) { - result(x, y) = val; - } - } - } - - // Outer X boundary - if (localmesh->lastX()) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(localmesh->xend + 1, y); - PetscScalar val; - VecGetValues(xs, 1, &ind, &val); - for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - result(x, y) = val; - } - } - } - - // Lower Y boundary - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - if ( - // Should not go into corner cells, finite-volume LaplaceXY stencil does not include - // them - (finite_volume and (it.ind < localmesh->xstart or it.ind > localmesh->xend)) - - // Only go into first corner cell for finite-difference - or (it.ind < localmesh->xstart - 1 or it.ind > localmesh->xend + 1)) { - continue; - } - int ind = globalIndex(it.ind, localmesh->ystart - 1); - PetscScalar val; - VecGetValues(xs, 1, &ind, &val); - for (int y = localmesh->ystart - 1; y >= 0; y--) { - result(it.ind, y) = val; - } - } - - // Upper Y boundary - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - if ( - // Should not go into corner cells, finite-volume LaplaceXY stencil does not include - // them - (finite_volume and (it.ind < localmesh->xstart or it.ind > localmesh->xend)) - - // Only go into first corner cell for finite-difference - or (it.ind < localmesh->xstart - 1 or it.ind > localmesh->xend + 1)) { - continue; - } - int ind = globalIndex(it.ind, localmesh->yend + 1); - PetscScalar val; - VecGetValues(xs, 1, &ind, &val); - for (int y = localmesh->yend + 1; y < localmesh->LocalNy; y++) { - result(it.ind, y) = val; - } - } - - return result; -} - -void LaplaceXY::solveFiniteVolume(const Field2D& x0) { - // Use original LaplaceXY implementation of passing boundary values for backward - // compatibility - if (localmesh->firstX()) { - if (x_inner_dirichlet) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(localmesh->xstart - 1, y); - - PetscScalar val = x0(localmesh->xstart - 1, y); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - val = 0.5 * (x0(localmesh->xstart - 1, y) + x0(localmesh->xstart, y)); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } else { - // Inner X boundary (Neumann) - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(localmesh->xstart - 1, y); - - PetscScalar val = x0(localmesh->xstart - 1, y); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - val = 0.0; // x0(localmesh->xstart-1,y) - x0(localmesh->xstart,y); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - } - - // Outer X boundary (Dirichlet) - if (localmesh->lastX()) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(localmesh->xend + 1, y); - - PetscScalar val = x0(localmesh->xend + 1, y); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - val = 0.5 * (x0(localmesh->xend, y) + x0(localmesh->xend + 1, y)); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - - if (y_bndry == "dirichlet") { - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->ystart - 1); - - PetscScalar val = x0(it.ind, localmesh->ystart - 1); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - val = 0.5 * (x0(it.ind, localmesh->ystart - 1) + x0(it.ind, localmesh->ystart)); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->yend + 1); - - PetscScalar val = x0(it.ind, localmesh->yend + 1); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - val = 0.5 * (x0(it.ind, localmesh->yend + 1) + x0(it.ind, localmesh->yend)); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } else if (y_bndry == "neumann" or y_bndry == "free_o3") { - // Y boundaries Neumann - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->ystart - 1); - - PetscScalar val = x0(it.ind, localmesh->ystart - 1); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->yend + 1); - - PetscScalar val = x0(it.ind, localmesh->yend + 1); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } else { - throw BoutException("Unsupported option for y_bndry"); - } -} - -void LaplaceXY::solveFiniteDifference(const Field2D& x0) { - // For finite-difference implementation pass boundary values in the same way as for - // the 'Laplacian' solvers - the value to use (for Dirichlet boundary conditions) on - // the boundary (which is half way between grid cell and boundary cell) is passed as - // the value in the first boundary cell. - if (localmesh->firstX()) { - if (x_inner_dirichlet) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(localmesh->xstart - 1, y); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 2. * x0(localmesh->xstart - 1, y) - x0(localmesh->xstart, y); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Pass the value from boundary cell of x0 as the boundary condition to the rhs - val = x0(localmesh->xstart - 1, y); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } else { - // Inner X boundary (Neumann) - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(localmesh->xstart - 1, y); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = x0(localmesh->xstart, y); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - } - - // Outer X boundary (Dirichlet) - if (localmesh->lastX()) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - int ind = globalIndex(localmesh->xend + 1, y); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 2. * x0(localmesh->xend + 1, y) - x0(localmesh->xend, y); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Pass the value from boundary cell of x0 as the boundary condition to the rhs - val = x0(localmesh->xend + 1, y); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - - if (y_bndry == "dirichlet") { - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, they are treated specially below - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->ystart - 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = - 2. * x0(it.ind, localmesh->ystart - 1) - x0(it.ind, localmesh->ystart); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Pass the value from boundary cell of x0 as the boundary condition to the rhs - val = x0(it.ind, localmesh->ystart - 1); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, they are treated specially below - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->yend + 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = - 2. * x0(it.ind, localmesh->yend + 1) - x0(it.ind, localmesh->yend); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Pass the value from boundary cell of x0 as the boundary condition to the rhs - val = x0(it.ind, localmesh->yend + 1); - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - - // Use free_o3 for the corner boundary cells - if (localmesh->hasBndryLowerY()) { - if (localmesh->firstX()) { - int ind = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(localmesh->xstart - 1, localmesh->ystart) - - 3. * x0(localmesh->xstart - 1, localmesh->ystart + 1) - + x0(localmesh->xstart - 1, localmesh->ystart + 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->lastX()) { - int ind = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(localmesh->xend + 1, localmesh->ystart) - - 3. * x0(localmesh->xend + 1, localmesh->ystart + 1) - + x0(localmesh->xend + 1, localmesh->ystart + 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - if (localmesh->hasBndryUpperY()) { - if (localmesh->firstX()) { - int ind = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(localmesh->xstart - 1, localmesh->yend) - - 3. * x0(localmesh->xstart - 1, localmesh->yend - 1) - + x0(localmesh->xstart - 1, localmesh->yend - 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->lastX()) { - int ind = globalIndex(localmesh->xend + 1, localmesh->yend + 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(localmesh->xend + 1, localmesh->yend) - - 3. * x0(localmesh->xend + 1, localmesh->yend - 1) - + x0(localmesh->xend + 1, localmesh->yend - 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - } else if (y_bndry == "neumann") { - // Y boundaries Neumann - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, they are treated specially below - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->ystart - 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = x0(it.ind, localmesh->ystart); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->hasBndryLowerY()) { - if (localmesh->firstX()) { - int ind = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = x0(localmesh->xstart - 1, localmesh->ystart); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->lastX()) { - int ind = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = x0(localmesh->xend + 1, localmesh->ystart); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, they are treated specially below - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->yend + 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = x0(it.ind, localmesh->yend); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->hasBndryUpperY()) { - if (localmesh->firstX()) { - int ind = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = x0(localmesh->xstart - 1, localmesh->yend); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->lastX()) { - int ind = globalIndex(localmesh->xend + 1, localmesh->yend + 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = x0(localmesh->xend + 1, localmesh->yend); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - } else if (y_bndry == "free_o3") { - // Y boundaries free_o3 - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, they are treated specially below - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->ystart - 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(it.ind, localmesh->ystart) - - 3. * x0(it.ind, localmesh->ystart + 1) - + x0(it.ind, localmesh->ystart + 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->hasBndryLowerY()) { - if (localmesh->firstX()) { - int ind = globalIndex(localmesh->xstart - 1, localmesh->ystart - 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(localmesh->xstart - 1, localmesh->ystart) - - 3. * x0(localmesh->xstart - 1, localmesh->ystart + 1) - + x0(localmesh->xstart - 1, localmesh->ystart + 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->lastX()) { - int ind = globalIndex(localmesh->xend + 1, localmesh->ystart - 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(localmesh->xend + 1, localmesh->ystart) - - 3. * x0(localmesh->xend + 1, localmesh->ystart + 1) - + x0(localmesh->xend + 1, localmesh->ystart + 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, they are treated specially below - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - int ind = globalIndex(it.ind, localmesh->yend + 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(it.ind, localmesh->yend) - - 3. * x0(it.ind, localmesh->yend - 1) - + x0(it.ind, localmesh->yend - 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->hasBndryUpperY()) { - if (localmesh->firstX()) { - int ind = globalIndex(localmesh->xstart - 1, localmesh->yend + 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(localmesh->xstart - 1, localmesh->yend) - - 3. * x0(localmesh->xstart - 1, localmesh->yend - 1) - + x0(localmesh->xstart - 1, localmesh->yend - 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - if (localmesh->lastX()) { - int ind = globalIndex(localmesh->xend + 1, localmesh->yend + 1); - - // For the boundary value of the initial guess, use the value that would be set by - // applying the boundary condition to the initial guess - PetscScalar val = 3. * x0(localmesh->xend + 1, localmesh->yend) - - 3. * x0(localmesh->xend + 1, localmesh->yend - 1) - + x0(localmesh->xend + 1, localmesh->yend - 2); - VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); - - // Set the value for the rhs of the boundary condition to zero - val = 0.0; - VecSetValues(bs, 1, &ind, &val, INSERT_VALUES); - } - } - } else { - throw BoutException("Unsupported option for y_bndry"); - } -} - -/*! Preconditioner - * NOTE: For generality, this routine does use globalIndex() in the inner loop, although - * this may be slightly less efficient than incrementing an integer for the global index, - * the finite-volume and finite-difference implementations have slightly different - * indexing patterns, so incrementing an integer would be tricky. - */ -int LaplaceXY::precon(Vec input, Vec result) { - - for (auto itdwn = localmesh->iterateBndryLowerY(); !itdwn.isDone(); itdwn++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (itdwn.ind < localmesh->xstart or itdwn.ind > localmesh->xend) { - continue; - } - const int ind = globalIndex(itdwn.ind, localmesh->ystart - 1); - PetscScalar val; - VecGetValues(input, 1, &ind, &val); - VecSetValues(result, 1, &ind, &val, INSERT_VALUES); - } - for (auto itup = localmesh->iterateBndryUpperY(); !itup.isDone(); itup++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (itup.ind < localmesh->xstart or itup.ind > localmesh->xend) { - continue; - } - const int ind = globalIndex(itup.ind, localmesh->yend + 1); - PetscScalar val; - VecGetValues(input, 1, &ind, &val); - VecSetValues(result, 1, &ind, &val, INSERT_VALUES); - } - - // Load vector x into bvals array - for (int x = xstart; x <= xend; x++) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int ind = globalIndex(x, y); - PetscScalar val; - VecGetValues(input, 1, &ind, &val); - bvals(y - localmesh->ystart, x - xstart) = val; - } - } - - // Solve tridiagonal systems using CR solver - cr->solve(bvals, xvals); - - // Save result xvals into y array - for (int x = xstart; x <= xend; x++) { - for (int y = localmesh->ystart; y <= localmesh->yend; y++) { - const int ind = globalIndex(x, y); - PetscScalar val = xvals(y - localmesh->ystart, x - xstart); - VecSetValues(result, 1, &ind, &val, INSERT_VALUES); - } - } - VecAssemblyBegin(result); - VecAssemblyEnd(result); - return 0; -} - -/////////////////////////////////////////////////////////////// - -int LaplaceXY::localSize() { - - // Bulk of points - const int nx = localmesh->xend - localmesh->xstart + 1; - const int ny = localmesh->yend - localmesh->ystart + 1; - - int n = nx * ny; - - // X boundaries - if (localmesh->firstX()) { - n += ny; - } - if (localmesh->lastX()) { - n += ny; - } - - // Y boundaries - for (RangeIterator it = localmesh->iterateBndryLowerY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - n++; - } - if ((not finite_volume) and localmesh->hasBndryLowerY()) { - if (localmesh->firstX()) { - n++; - } - if (localmesh->lastX()) { - n++; - } - } - for (RangeIterator it = localmesh->iterateBndryUpperY(); !it.isDone(); it++) { - // Should not go into corner cells, LaplaceXY stencil does not include them - if (it.ind < localmesh->xstart or it.ind > localmesh->xend) { - continue; - } - n++; - } - if ((not finite_volume) and localmesh->hasBndryUpperY()) { - if (localmesh->firstX()) { - n++; - } - if (localmesh->lastX()) { - n++; - } - } - - return n; -} - -int LaplaceXY::globalIndex(int x, int y) { - if ((x < 0) || (x >= localmesh->LocalNx) || (y < 0) || (y >= localmesh->LocalNy)) { - return -1; // Out of range - } - - // Get the index from a Field2D, round to integer - return static_cast(std::round(indexXY(x, y))); -} - -void LaplaceXY::savePerformance(Solver& solver, const std::string& name) { - // set flag so that performance monitoring values are calculated - save_performance = true; - - // add values to be saved to the output - if (not name.empty()) { - default_prefix = name; - } - - // add monitor to reset counters/averages for new output timestep - // monitor added to back of queue, so that values are reset after being saved - solver.addMonitor(&monitor, Solver::BACK); -} - -int LaplaceXY::LaplaceXYMonitor::call(Solver* /*solver*/, BoutReal /*time*/, int /*iter*/, - int /*nout*/) { - laplacexy.output_average_iterations = laplacexy.average_iterations; - - laplacexy.n_calls = 0; - laplacexy.average_iterations = 0.; - - return 0; -} - -void LaplaceXY::LaplaceXYMonitor::outputVars(Options& output_options, - const std::string& time_dimension) { - output_options[fmt::format("{}_average_iterations", laplacexy.default_prefix)] - .assignRepeat(laplacexy.output_average_iterations, time_dimension); -} - -#endif // BOUT_HAS_PETSC +// DO NOT REMOVE: ensures linker keeps all symbols in this TU +void LaplaceXYFactory::ensureRegistered() {} diff --git a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx index a242bf3432..b3e619df0c 100644 --- a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx +++ b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -14,6 +13,7 @@ LaplaceXZcyclic::LaplaceXZcyclic(Mesh* m, Options* options, const CELL_LOC loc) : LaplaceXZ(m, options, loc) { // Note: `m` may be nullptr, but localmesh is set in LaplaceXZ base constructor + bout::fft::assertZSerial(*localmesh, "`cyclic` X-Z inversion"); // Number of Z Fourier modes, including DC nmode = (localmesh->LocalNz) / 2 + 1; @@ -61,7 +61,7 @@ LaplaceXZcyclic::LaplaceXZcyclic(Mesh* m, Options* options, const CELL_LOC loc) } void LaplaceXZcyclic::setCoefs(const Field2D& A2D, const Field2D& B2D) { - TRACE("LaplaceXZcyclic::setCoefs"); + Timer timer("invert"); ASSERT1(A2D.getMesh() == localmesh); diff --git a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx index 769e797352..3e55262b6d 100644 --- a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx +++ b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx @@ -15,10 +15,8 @@ #if BOUT_HAS_PETSC // Requires PETSc #include -#include - -#include #include +#include LaplaceXZpetsc::LaplaceXZpetsc(Mesh* m, Options* opt, const CELL_LOC loc) : LaplaceXZ(m, opt, loc), lib(opt == nullptr ? &(Options::root()["laplacexz"]) : opt), @@ -97,8 +95,6 @@ LaplaceXZpetsc::LaplaceXZpetsc(Mesh* m, Options* opt, const CELL_LOC loc) * given by b */ - TRACE("LaplaceXZpetsc::LaplaceXZpetsc"); - if (opt == nullptr) { // If no options supplied, use default opt = &(Options::root())["laplacexz"]; @@ -159,7 +155,7 @@ LaplaceXZpetsc::LaplaceXZpetsc(Mesh* m, Options* opt, const CELL_LOC loc) .withDefault("petsc"); // Get MPI communicator - MPI_Comm comm = localmesh->getXcomm(); + MPI_Comm comm = localmesh->getXZcomm(); // Local size int localN = (localmesh->xend - localmesh->xstart + 1) * (localmesh->LocalNz); @@ -202,25 +198,25 @@ LaplaceXZpetsc::LaplaceXZpetsc(Mesh* m, Options* opt, const CELL_LOC loc) // X boundaries if (localmesh->firstX()) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { d_nnz[z] = 2; } } else { // One point on another processor - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { d_nnz[z] -= 1; o_nnz[z] += 1; } } if (localmesh->lastX()) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { int ind = localN - (localmesh->LocalNz) + z; d_nnz[ind] = 2; } } else { // One point on another processor - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { int ind = localN - (localmesh->LocalNz) + z; d_nnz[ind] -= 1; o_nnz[ind] += 1; @@ -260,8 +256,6 @@ LaplaceXZpetsc::LaplaceXZpetsc(Mesh* m, Options* opt, const CELL_LOC loc) LaplaceXZpetsc::~LaplaceXZpetsc() { - TRACE("LaplaceXZpetsc::~LaplaceXZpetsc"); - PetscBool petsc_is_finalised; PetscFinalized(&petsc_is_finalised); @@ -293,8 +287,6 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { * Bin - The B coefficient in div(A grad_perp(B)) + Bf = b */ - TRACE("LaplaceXZpetsc::setCoefs"); - ASSERT1(Ain.getMesh() == localmesh); ASSERT1(Bin.getMesh() == localmesh); ASSERT1(Ain.getLocation() == location); @@ -335,7 +327,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { /* NOTE: Sign of the elements are opposite of what one might expect, * see note about BC in LaplaceXZ constructor for more details */ - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -347,7 +339,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { } } else if (inner_boundary_flags & INVERT_SET) { // Setting BC from x0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -359,7 +351,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { } } else if (inner_boundary_flags & INVERT_RHS) { // Setting BC from b - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -374,7 +366,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { /* NOTE: Sign of the elements are opposite of what one might expect, * see note about BC in LaplaceXZ constructor for more details */ - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 0.5; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -393,7 +385,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { Coordinates* coords = localmesh->getCoordinates(location); for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // stencil entries PetscScalar c, xm, xp, zm, zp; // Diagonal entries @@ -655,7 +647,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { if (localmesh->lastX()) { if (outer_boundary_flags & INVERT_AC_GRAD) { // Neumann 0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -667,7 +659,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { } } else if (outer_boundary_flags & INVERT_SET) { // Setting BC from x0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -679,7 +671,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { } } else if (outer_boundary_flags & INVERT_RHS) { // Setting BC from b - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -693,7 +685,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { //Default: Dirichlet on outer X boundary PetscScalar val = 0.5; - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); int col = row - (localmesh->LocalNz); // -1 in X @@ -767,8 +759,6 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { * result - The solved x (returned as a Field3D) in the matrix problem Ax=b */ - TRACE("LaplaceXZpetsc::solve"); - ASSERT1(bin.getMesh() == localmesh); ASSERT1(x0in.getMesh() == localmesh); ASSERT1(bin.getLocation() == location); @@ -804,7 +794,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { if (localmesh->firstX()) { if (inner_boundary_flags & INVERT_AC_GRAD) { // Neumann 0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xstart - 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -816,7 +806,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else if (inner_boundary_flags & INVERT_SET) { // Setting BC from x0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xstart - 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -828,7 +818,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else if (inner_boundary_flags & INVERT_RHS) { // Setting BC from b - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xstart - 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -840,7 +830,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else { // Default: Neumann on inner x boundary - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xstart - 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -855,7 +845,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { // Set the inner points for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = x0(x, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -869,7 +859,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { if (localmesh->lastX()) { if (outer_boundary_flags & INVERT_AC_GRAD) { // Neumann 0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xend + 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -882,7 +872,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else if (outer_boundary_flags & INVERT_SET) { // Setting BC from x0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xend + 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -895,7 +885,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else if (outer_boundary_flags & INVERT_RHS) { // Setting BC from b - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xend + 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -908,7 +898,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else { //Default: Dirichlet on outer X boundary - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xend + 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -952,7 +942,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { ind = Istart; // Inner X boundary if (localmesh->firstX()) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; VecGetValues(xs, 1, &ind, &val); for (int x = localmesh->xstart - 1; x >= 0; --x) { @@ -963,7 +953,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; VecGetValues(xs, 1, &ind, &val); result(x, y, z) = val; @@ -973,7 +963,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { // Outer X boundary if (localmesh->lastX()) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; VecGetValues(xs, 1, &ind, &val); for (int x = localmesh->xend + 1; x < localmesh->LocalNx; ++x) { diff --git a/src/invert/parderiv/impls/cyclic/cyclic.cxx b/src/invert/parderiv/impls/cyclic/cyclic.cxx index 5a798ca1d5..c32c3d4b2d 100644 --- a/src/invert/parderiv/impls/cyclic/cyclic.cxx +++ b/src/invert/parderiv/impls/cyclic/cyclic.cxx @@ -46,15 +46,15 @@ #include #include #include -#include -#include - #include +#include #include InvertParCR::InvertParCR(Options* opt, CELL_LOC location, Mesh* mesh_in) : InvertPar(opt, location, mesh_in), A(1.0), B(0.0), C(0.0), D(0.0), E(0.0) { + + bout::fft::assertZSerial(*localmesh, "InvertParCR"); // Number of k equations to solve for each x location nsys = 1 + (localmesh->LocalNz) / 2; @@ -63,7 +63,7 @@ InvertParCR::InvertParCR(Options* opt, CELL_LOC location, Mesh* mesh_in) } const Field3D InvertParCR::solve(const Field3D& f) { - TRACE("InvertParCR::solve(Field3D)"); + ASSERT1(localmesh == f.getMesh()); ASSERT1(location == f.getLocation()); diff --git a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx index be65d5ab0b..aad01c5f2f 100644 --- a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx +++ b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx @@ -46,7 +46,6 @@ #include #include #include -#include #include #include @@ -54,12 +53,13 @@ InvertParDivCR::InvertParDivCR(Options* opt, CELL_LOC location, Mesh* mesh_in) : InvertParDiv(opt, location, mesh_in) { + bout::fft::assertZSerial(*localmesh, "InvertParDivCR"); // Number of k equations to solve for each x location nsys = 1 + (localmesh->LocalNz) / 2; } Field3D InvertParDivCR::solve(const Field3D& f) { - TRACE("InvertParDivCR::solve(Field3D)"); + ASSERT1(localmesh == f.getMesh()); ASSERT1(location == f.getLocation()); diff --git a/src/mesh/boundary_factory.cxx b/src/mesh/boundary_factory.cxx index d112a216ad..988fcfce9b 100644 --- a/src/mesh/boundary_factory.cxx +++ b/src/mesh/boundary_factory.cxx @@ -1,11 +1,14 @@ +#include "bout/assert.hxx" #include "bout/parallel_boundary_op.hxx" #include "bout/parallel_boundary_region.hxx" #include +#include #include #include #include #include +#include #include #include #include @@ -104,7 +107,8 @@ BoundaryOpBase* BoundaryFactory::create(const string& name, BoundaryRegionBase* // Clone the boundary operation, passing the region to operate over, // an empty args list and empty keyword map list args; - return pop->clone(dynamic_cast(region), args, {}); + return pop->clone(dynamic_cast(region), args, + {}); } else { // Perpendicular boundary BoundaryOp* op = findBoundaryOp(trim(name)); @@ -115,7 +119,7 @@ BoundaryOpBase* BoundaryFactory::create(const string& name, BoundaryRegionBase* // Clone the boundary operation, passing the region to operate over, // an empty args list and empty keyword map list args; - return op->clone(dynamic_cast(region), args, {}); + return op->clone(region->getLegacyPointer(), args, {}); } } // Contains a bracket. Find the last bracket and remove @@ -191,19 +195,28 @@ BoundaryOpBase* BoundaryFactory::create(const string& name, BoundaryRegionBase* return mod->cloneMod(op, arglist); } - if (region->isParallel) { - // Parallel boundary - BoundaryOpPar* pop = findBoundaryOpPar(trim(func)); - if (pop != nullptr) { - // An operation with arguments - return pop->clone(dynamic_cast(region), arglist, keywords); + BoundaryOpPar* pop = findBoundaryOpPar(trim(func)); + if (pop != nullptr) { + // An operation with arguments + if (region->isParallel) { + return pop->clone(dynamic_cast(region), arglist, + keywords); } - } else { - // Perpendicular boundary + if (region->isX) { + return pop->clone(dynamic_cast(region), arglist, + keywords); + } + if (region->isY) { + return pop->clone(dynamic_cast(region), arglist, + keywords); + } + } + if (!region->isParallel) { + // Legacy perpendicular boundary BoundaryOp* op = findBoundaryOp(trim(func)); if (op != nullptr) { // An operation with arguments - return op->clone(dynamic_cast(region), arglist, keywords); + return op->clone(region->getLegacyPointer(), arglist, keywords); } } @@ -228,46 +241,83 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string& varname, string prefix("bndry_"); - string side; + std::array sides; + sides[0] = region->label; + ASSERT2(region->location != BNDRY_INVALID) switch (region->location) { case BNDRY_XIN: { - side = "xin"; + sides[1] = "xin"; break; } case BNDRY_XOUT: { - side = "xout"; + sides[1] = "xout"; break; } case BNDRY_YDOWN: { - side = "ydown"; + sides[1] = "ydown"; break; } case BNDRY_YUP: { - side = "yup"; + sides[1] = "yup"; break; } case BNDRY_PAR_FWD_XIN: { - side = "par_yup_xin"; + sides[1] = "par_yup_xin"; break; } case BNDRY_PAR_FWD_XOUT: { - side = "par_yup_xout"; + sides[1] = "par_yup_xout"; break; } case BNDRY_PAR_BKWD_XIN: { - side = "par_ydown_xin"; + sides[1] = "par_ydown_xin"; break; } case BNDRY_PAR_BKWD_XOUT: { - side = "par_ydown_xout"; + sides[1] = "par_ydown_xout"; break; } default: { - side = "all"; + sides[1] = "all"; break; } } + switch (region->location) { + case BNDRY_PAR_FWD_XIN: + case BNDRY_PAR_BKWD_XIN: { + sides[2] = "par_xin"; + break; + } + case BNDRY_PAR_BKWD_XOUT: + case BNDRY_PAR_FWD_XOUT: { + sides[2] = "par_xout"; + break; + } + default: { + sides[2] = "all"; + break; + } + } + switch (region->location) { + case BNDRY_PAR_FWD_XIN: + case BNDRY_PAR_FWD_XOUT: { + sides[3] = "par_yup"; + break; + } + case BNDRY_PAR_BKWD_XIN: + case BNDRY_PAR_BKWD_XOUT: { + sides[3] = "par_ydown"; + break; + } + default: { + sides[3] = "all"; + break; + } + } + + sides[4] = region->isParallel ? "par_all" : "all"; + // Get options Options* options = Options::getRoot(); @@ -275,27 +325,10 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string& varname, Options* varOpts = options->getSection(varname); string set; - /// First try looking for (var, region) - if (varOpts->isSet(prefix + region->label)) { - varOpts->get(prefix + region->label, set, ""); - return create(set, region); - } - - /// Then (var, side) - if (varOpts->isSet(prefix + side)) { - varOpts->get(prefix + side, set, ""); - return create(set, region); - } - - /// Then (var, all) - if (region->isParallel) { - if (varOpts->isSet(prefix + "par_all")) { - varOpts->get(prefix + "par_all", set, ""); - return create(set, region); - } - } else { - if (varOpts->isSet(prefix + "all")) { - varOpts->get(prefix + "all", set, ""); + /// First try looking for (var, ...) + for (const auto& side : sides) { + if (varOpts->isSet(prefix + side)) { + varOpts->get(prefix + side, set, ""); return create(set, region); } } @@ -303,16 +336,13 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string& varname, // Get the "all" options varOpts = options->getSection("all"); - /// Then (all, region) - if (varOpts->isSet(prefix + region->label)) { - varOpts->get(prefix + region->label, set, ""); - return create(set, region); - } - - /// Then (all, side) - if (varOpts->isSet(prefix + side)) { - varOpts->get(prefix + side, set, ""); - return create(set, region); + /// First try looking for (all, ...) + for (const auto& side : sides) { + if (varOpts->isSet(prefix + side)) { + varOpts->get(prefix + side, set, + region->isParallel ? "parallel_dirichlet_o2" : "dirichlet"); + return create(set, region); + } } /// Then (all, all) diff --git a/src/mesh/boundary_region.cxx b/src/mesh/boundary_region.cxx index 700ef8a91f..2dc64fc88f 100644 --- a/src/mesh/boundary_region.cxx +++ b/src/mesh/boundary_region.cxx @@ -1,4 +1,6 @@ +#include "bout/assert.hxx" +#include "bout/boutexception.hxx" #include #include #include @@ -7,6 +9,16 @@ #include using std::swap; +BoundaryRegion* BoundaryRegionBase::getLegacyPointer() { + if (legacy == nullptr) { + throw BoutException("Legacy region not supported"); + } + ASSERT3(legacy->location == location); + ASSERT3(legacy->label == label); + ASSERT3(legacy->localmesh == localmesh); + return legacy; +} +BoundaryRegionBase::~BoundaryRegionBase() { delete legacy; }; BoundaryRegionXIn::BoundaryRegionXIn(std::string name, int ymin, int ymax, Mesh* passmesh) : BoundaryRegion(std::move(name), -1, 0, passmesh), ys(ymin), ye(ymax) { location = BNDRY_XIN; diff --git a/src/mesh/boundary_standard.cxx b/src/mesh/boundary_standard.cxx index c12901e53a..2e079f59b3 100644 --- a/src/mesh/boundary_standard.cxx +++ b/src/mesh/boundary_standard.cxx @@ -1,13 +1,14 @@ #include #include +#include #include #include #include #include #include #include -#include #include +#include #include using bout::generator::Context; @@ -28,8 +29,6 @@ using bout::generator::Context; */ #if CHECK > 0 void verifyNumPoints(BoundaryRegion* region, int ptsRequired) { - TRACE("Verifying number of points available for BC"); - int ptsAvailGlobal, ptsAvailLocal, ptsAvail; std::string side, gridType; Mesh* mesh = region->localmesh; @@ -324,7 +323,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { // Outer x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -345,7 +344,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { if (bndry->bx < 0) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -367,7 +366,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { if (bndry->by != 0) { // y boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -393,7 +392,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { // Upper y boundary boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -414,7 +413,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { if (bndry->by < 0) { // Lower y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -435,7 +434,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { if (bndry->bx != 0) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -460,18 +459,18 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { for (; !bndry->isDone(); bndry->next1d()) { // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5 - * (mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx)); // the grid cell + const BoutReal xnorm = 0.5 + * (mesh->GlobalX(bndry->x) // In the guard cell + + mesh->GlobalX(bndry->x - bndry->bx)); // the grid cell - BoutReal ynorm = 0.5 - * (mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell + const BoutReal ynorm = TWOPI * 0.5 + * (mesh->GlobalY(bndry->y) // In the guard cell + + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { - val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * (zk - 0.5) / (mesh->LocalNz), - t); + val = fg->generate( + Context(bndry, zk, loc, t, mesh).set("x", xnorm, "y", ynorm)); } f(bndry->x, bndry->y, zk) = 2 * val - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk); @@ -508,14 +507,14 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { // can help with the stability of higher order methods. for (int i = 1; i < bndry->width; i++) { // Set any other guard cells using the values on the cells - int xi = bndry->x + i * bndry->bx; - int yi = bndry->y + i * bndry->by; - xnorm = mesh->GlobalX(xi); - ynorm = mesh->GlobalY(yi); - for (int zk = 0; zk < mesh->LocalNz; zk++) { + const int xi = bndry->x + (i * bndry->bx); + const int yi = bndry->y + (i * bndry->by); + const auto xnorm = mesh->GlobalX(xi); + const auto ynorm = mesh->GlobalY(yi) * TWOPI; + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { - val = fg->generate(xnorm, TWOPI * ynorm, - TWOPI * (zk - 0.5) / (mesh->LocalNz), t); + val = fg->generate( + Context(bndry, zk, loc, t, mesh).set("x", xnorm, "y", ynorm)); } f(xi, yi, zk) = val; } @@ -527,7 +526,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { } else { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -571,7 +570,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { // Set any other guard cells using the values on the cells int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -595,7 +594,7 @@ void BoundaryDirichlet::apply_ddt(Field3D& f) { Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -826,7 +825,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { // Outer x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -847,7 +846,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { if (bndry->bx < 0) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -869,7 +868,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { // y boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -897,7 +896,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { // Upper y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -918,7 +917,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { if (bndry->by < 0) { // Lower y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -939,7 +938,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { if (bndry->bx != 0) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -965,18 +964,18 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { for (; !bndry->isDone(); bndry->next1d()) { // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5 - * (mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx)); // the grid cell + const BoutReal xnorm = 0.5 + * (mesh->GlobalX(bndry->x) // In the guard cell + + mesh->GlobalX(bndry->x - bndry->bx)); // the grid cell - BoutReal ynorm = 0.5 - * (mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell + const BoutReal ynorm = TWOPI * 0.5 + * (mesh->GlobalY(bndry->y) // In the guard cell + + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { - val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * (zk - 0.5) / (mesh->LocalNz), - t); + val = fg->generate( + Context(bndry, zk, loc, t, mesh).set("x", xnorm, "y", ynorm)); } f(bndry->x, bndry->y, zk) = @@ -999,7 +998,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { } else { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1037,7 +1036,7 @@ void BoundaryDirichlet_O3::apply_ddt(Field3D& f) { bndry->first(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -1283,7 +1282,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { // Outer x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1305,7 +1304,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { if (bndry->bx < 0) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1328,7 +1327,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { // y boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1357,7 +1356,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { // Outer y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1380,7 +1379,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { if (bndry->by < 0) { // Inner y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1404,7 +1403,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1431,18 +1430,18 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { // Shifted in Z for (; !bndry->isDone(); bndry->next1d()) { // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5 - * (mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx)); // the grid cell + const BoutReal xnorm = 0.5 + * (mesh->GlobalX(bndry->x) // In the guard cell + + mesh->GlobalX(bndry->x - bndry->bx)); // the grid cell - BoutReal ynorm = 0.5 - * (mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell + const BoutReal ynorm = TWOPI * 0.5 + * (mesh->GlobalY(bndry->y) // In the guard cell + + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { - val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * (zk - 0.5) / (mesh->LocalNz), - t); + val = fg->generate( + Context(bndry, zk, loc, t, mesh).set("x", xnorm, "y", ynorm)); } f(bndry->x, bndry->y, zk) = @@ -1467,7 +1466,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { } else { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1504,7 +1503,7 @@ void BoundaryDirichlet_O4::apply_ddt(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -1546,7 +1545,7 @@ void BoundaryDirichlet_4thOrder::apply(Field3D& f) { // Set (at 4th order) the value at the mid-point between the guard cell and the grid // cell to be val for (bndry->first(); !bndry->isDone(); bndry->next1d()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = 128. / 35. * val - 4. * f(bndry->x - bndry->bx, bndry->y - bndry->by, z) + 2. * f(bndry->x - 2 * bndry->bx, bndry->y - 2 * bndry->by, z) @@ -1573,7 +1572,7 @@ void BoundaryDirichlet_4thOrder::apply_ddt(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -1660,7 +1659,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Loop over all elements and set equal to the next point in for (bndry->first(); !bndry->isDone(); bndry->next1d()) { #if BOUT_USE_METRIC_3D - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { #else int z = 0; #endif @@ -1679,7 +1678,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // because derivative values don't exist in boundary region // NOTE: should be fixed to interpolate to boundary line #if not(BOUT_USE_METRIC_3D) - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { #endif BoutReal xshift = g12shift * dfdy(bndry->x - bndry->bx, bndry->y, z) + g13shift * dfdz(bndry->x - bndry->bx, bndry->y, z); @@ -1967,7 +1966,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->bx > 0) { // Outer x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dx(bndry->x, bndry->y, zk); @@ -1996,7 +1995,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->bx < 0) { // Inner x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dx(bndry->x - bndry->bx, bndry->y, zk); @@ -2028,7 +2027,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) + bndry->by * metric->dy(bndry->x, bndry->y); #endif - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { #if BOUT_USE_METRIC_3D BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); @@ -2052,7 +2051,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->by > 0) { // Outer y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dy(bndry->x, bndry->y, zk); @@ -2080,7 +2079,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->by < 0) { // Inner y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dy(bndry->x, bndry->y - bndry->by, zk); @@ -2114,7 +2113,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); #endif - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { #if BOUT_USE_METRIC_3D BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); @@ -2137,20 +2136,22 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { for (; !bndry->isDone(); bndry->next1d()) { // Calculate the X and Y normalised values half-way between the guard cell and // grid cell - BoutReal xnorm = 0.5 - * (mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx)); // the grid cell + const BoutReal xnorm = + 0.5 + * (mesh->GlobalX(bndry->x) // In the guard cell + + mesh->GlobalX(bndry->x - bndry->bx)); // the grid cell - BoutReal ynorm = 0.5 - * (mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell + const BoutReal ynorm = + TWOPI * 0.5 + * (mesh->GlobalY(bndry->y) // In the guard cell + + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); if (fg) { - val = fg->generate(xnorm, TWOPI * ynorm, - TWOPI * (zk - 0.5) / (mesh->LocalNz), t); + val = fg->generate( + Context(bndry, zk, loc, t, mesh).set("x", xnorm, "y", ynorm)); } f(bndry->x, bndry->y, zk) = f(bndry->x - bndry->bx, bndry->y - bndry->by, zk) + delta * val; @@ -2164,16 +2165,16 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { } else { throw BoutException("Unrecognized location"); } - } else { + } else { // loc == CELL_CENTRE for (; !bndry->isDone(); bndry->next1d()) { #if BOUT_USE_METRIC_3D - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); #else BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) + bndry->by * metric->dy(bndry->x, bndry->y); - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { #endif if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); @@ -2202,7 +2203,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -2300,7 +2301,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { } else { Coordinates* coords = f.getCoordinates(); for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { BoutReal delta = bndry->bx * coords->dx(bndry->x, bndry->y, zk) + bndry->by * coords->dy(bndry->x, bndry->y, zk); if (fg) { @@ -2336,7 +2337,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -2395,7 +2396,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // grid cell to be val This sets the value of the co-ordinate derivative, i.e. DDX/DDY // not Grad_par/Grad_perp.x for (bndry->first(); !bndry->isDone(); bndry->next1d()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { BoutReal delta = -(bndry->bx * metric->dx(bndry->x, bndry->y, z) + bndry->by * metric->dy(bndry->x, bndry->y, z)); f(bndry->x, bndry->y, z) = @@ -2428,7 +2429,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -2466,7 +2467,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Coordinates* metric = f.getCoordinates(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = f(bndry->x - bndry->bx, bndry->y - bndry->by, z) * sqrt(metric->g_22(bndry->x, bndry->y, z) @@ -2533,7 +2534,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); if (fabs(bval) < 1.e-12) { for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = gval / aval; } } @@ -2543,7 +2544,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { sign = -1.; } for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = f(bndry->x - bndry->bx, bndry->y - bndry->by, z) + sign * (gval - aval * f(bndry->x - bndry->bx, bndry->y - bndry->by, z)) @@ -2567,7 +2568,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = 2. * f(bndry->x - bndry->bx, bndry->y - bndry->by, z) - f(bndry->x - 2 * bndry->bx, bndry->y - 2 * bndry->by, z); @@ -2630,6 +2631,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { #if not(BOUT_USE_METRIC_3D) Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); + bout::fft::assertZSerial(*mesh, "Zero Laplace on Field3D"); int ncz = mesh->LocalNz; Coordinates* metric = f.getCoordinates(); @@ -2733,6 +2735,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { #if not(BOUT_USE_METRIC_3D) Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); + bout::fft::assertZSerial(*mesh, "Zero Laplace on Field3D"); const int ncz = mesh->LocalNz; ASSERT0(ncz % 2 == 0); // Allocation assumes even number @@ -2842,6 +2845,8 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); + bout::fft::assertZSerial(*mesh, "Zero Laplace on Field3D"); + Coordinates* metric = f.getCoordinates(); int ncz = mesh->LocalNz; @@ -3188,7 +3193,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3202,7 +3207,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = -1; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3217,7 +3222,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3233,7 +3238,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->by > 0) { // Upper y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3247,7 +3252,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Lower y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = -1; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3261,7 +3266,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3276,7 +3281,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3300,7 +3305,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -3450,7 +3455,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3465,7 +3470,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = -1; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3481,7 +3486,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3498,7 +3503,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->by > 0) { // Upper y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3513,7 +3518,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Lower y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = -1; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3528,7 +3533,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3544,7 +3549,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3569,7 +3574,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -3602,7 +3607,6 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { } void BoundaryRelax::apply_ddt(Field2D & f) { - TRACE("BoundaryRelax::apply_ddt(Field2D)"); // Make a copy of f Field2D g = f; @@ -3618,7 +3622,6 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { } void BoundaryRelax::apply_ddt(Field3D & f) { - TRACE("BoundaryRelax::apply_ddt(Field3D)"); Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); @@ -3629,7 +3632,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { op->apply(g); // Set time-derivatives for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { ddt(f)(bndry->x, bndry->y, z) = r * (g(bndry->x, bndry->y, z) - f(bndry->x, bndry->y, z)); } diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 34c524d1e7..057ffa65b5 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -4,38 +4,37 @@ * given the contravariant metric tensor terms **************************************************************************/ +#include "bout/coordinates_accessor.hxx" +#include "bout/field3d.hxx" +#include "bout/field_data.hxx" #include +#include +#include +#include #include #include -#include -#include -#include -#include - #include #include +#include +#include #include +#include #include +#include +#include +#include +#include +#include -#include +#include +#include +#include #include "invert3x3.hxx" #include "parallel/fci.hxx" #include "parallel/shiftedmetricinterp.hxx" -// use anonymous namespace so this utility function is not available outside this file namespace { -template -// Use sendY()/sendX() and wait() instead of Mesh::communicate() to ensure we -// don't try to calculate parallel slices as Coordinates are not constructed yet -void communicate(T& t, Ts&... ts) { - FieldGroup g(t, ts...); - auto h = t.getMesh()->sendY(g); - t.getMesh()->wait(h); - h = t.getMesh()->sendX(g); - t.getMesh()->wait(h); -} - /// Interpolate a Field2D to a new CELL_LOC with interp_to. /// Communicates to set internal guard cells. /// Boundary guard cells are set by extrapolating from the grid, like @@ -55,7 +54,7 @@ Field2D interpolateAndExtrapolate(const Field2D& f, CELL_LOC location, bool extr // communicate f. We will sort out result's boundary guard cells below, but // not f's so we don't want to change f. result.allocate(); - communicate(result); + localmesh->communicate_no_slices(result); // Extrapolate into boundaries (if requested) so that differential geometry // terms can be interpolated if necessary @@ -64,16 +63,16 @@ Field2D interpolateAndExtrapolate(const Field2D& f, CELL_LOC location, bool extr // initializing yet, leading to an infinite recursion. // Also, here we interpolate for the boundary points at xstart/ystart and // (xend+1)/(yend+1) instead of extrapolating. - for (auto& bndry : localmesh->getBoundaries()) { + for (auto& newbndry : localmesh->getBoundaries()) { + auto* bndry = newbndry->getLegacyPointer(); if ((extrapolate_x and bndry->bx != 0) or (extrapolate_y and bndry->by != 0)) { int extrap_start = 0; if (not no_extra_interpolate) { // Can use no_extra_interpolate argument to skip the extra interpolation when we // want to extrapolate the Christoffel symbol terms which come from derivatives so // don't have the extra point set already - if ((location == CELL_XLOW) && (bndry->bx > 0)) { - extrap_start = 1; - } else if ((location == CELL_YLOW) && (bndry->by > 0)) { + if (((location == CELL_XLOW) && (bndry->bx > 0)) + || ((location == CELL_YLOW) && (bndry->by > 0))) { extrap_start = 1; } } @@ -92,7 +91,7 @@ Field2D interpolateAndExtrapolate(const Field2D& f, CELL_LOC location, bool extr (9. * (f(bndry->x - bndry->bx, bndry->y - bndry->by) + f(bndry->x, bndry->y)) - - f(bndry->x - 2 * bndry->bx, bndry->y - 2 * bndry->by) + - f(bndry->x - (2 * bndry->bx), bndry->y - (2 * bndry->by)) - f(bndry->x + bndry->bx, bndry->y + bndry->by)) / 16.; } @@ -118,8 +117,8 @@ Field2D interpolateAndExtrapolate(const Field2D& f, CELL_LOC location, bool extr } // extrapolate into boundary guard cells if there are enough grid points for (int i = extrap_start; i < bndry->width; i++) { - int xi = bndry->x + i * bndry->bx; - int yi = bndry->y + i * bndry->by; + const int xi = bndry->x + (i * bndry->bx); + const int yi = bndry->y + (i * bndry->by); result(xi, yi) = 3.0 * result(xi - bndry->bx, yi - bndry->by) - 3.0 * result(xi - 2 * bndry->bx, yi - 2 * bndry->by) + result(xi - 3 * bndry->bx, yi - 3 * bndry->by); @@ -204,7 +203,7 @@ Field3D interpolateAndExtrapolate(const Field3D& f_, CELL_LOC location, // communicate f. We will sort out result's boundary guard cells below, but // not f's so we don't want to change f. result.allocate(); - communicate(result); + localmesh->communicate_no_slices(result); // Extrapolate into boundaries (if requested) so that differential geometry // terms can be interpolated if necessary @@ -213,7 +212,8 @@ Field3D interpolateAndExtrapolate(const Field3D& f_, CELL_LOC location, // initializing yet, leading to an infinite recursion. // Also, here we interpolate for the boundary points at xstart/ystart and // (xend+1)/(yend+1) instead of extrapolating. - for (auto& bndry : localmesh->getBoundaries()) { + for (auto& newbndry : localmesh->getBoundaries()) { + auto bndry = newbndry->getLegacyPointer(); if ((extrapolate_x and bndry->bx != 0) or (extrapolate_y and bndry->by != 0)) { int extrap_start = 0; if (not no_extra_interpolate) { @@ -237,7 +237,7 @@ Field3D interpolateAndExtrapolate(const Field3D& f_, CELL_LOC location, ASSERT1(bndry->bx == 0 or localmesh->xstart > 1); ASSERT1(bndry->by == 0 or localmesh->ystart > 1); // note that either bx or by is >0 here - for (int zi = 0; zi < localmesh->LocalNz; ++zi) { + for (int zi = localmesh->zstart; zi <= localmesh->zend; ++zi) { result(bndry->x, bndry->y, zi) = (9. * (f(bndry->x - bndry->bx, bndry->y - bndry->by, zi) @@ -268,7 +268,7 @@ Field3D interpolateAndExtrapolate(const Field3D& f_, CELL_LOC location, for (int i = extrap_start; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; - for (int zi = 0; zi < localmesh->LocalNz; ++zi) { + for (int zi = localmesh->zstart; zi <= localmesh->zend; ++zi) { result(xi, yi, zi) = 3.0 * result(xi - bndry->bx, yi - bndry->by, zi) - 3.0 * result(xi - 2 * bndry->bx, yi - 2 * bndry->by, zi) @@ -278,7 +278,7 @@ Field3D interpolateAndExtrapolate(const Field3D& f_, CELL_LOC location, } else { // not enough grid points to extrapolate, set equal to last grid point for (int i = extrap_start; i < bndry->width; i++) { - for (int zi = 0; zi < localmesh->LocalNz; ++zi) { + for (int zi = localmesh->zstart; zi <= localmesh->zend; ++zi) { result(bndry->x + i * bndry->bx, bndry->y + i * bndry->by, zi) = result(bndry->x - bndry->bx, bndry->y - bndry->by, zi); } @@ -381,7 +381,7 @@ Coordinates::Coordinates(Mesh* mesh, FieldMetric dx, FieldMetric dy, FieldMetric g_22(std::move(g_22)), g_33(std::move(g_33)), g_12(std::move(g_12)), g_13(std::move(g_13)), g_23(std::move(g_23)), ShiftTorsion(std::move(ShiftTorsion)), IntShiftTorsion(std::move(IntShiftTorsion)), nz(mesh->LocalNz), localmesh(mesh), - location(CELL_CENTRE) {} + localoptions(nullptr), location(CELL_CENTRE) {} Coordinates::Coordinates(Mesh* mesh, Options* options) : dx(1., mesh), dy(1., mesh), dz(1., mesh), d1_dx(mesh), d1_dy(mesh), d1_dz(mesh), @@ -393,7 +393,8 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) G1_13(mesh), G1_23(mesh), G2_11(mesh), G2_22(mesh), G2_33(mesh), G2_12(mesh), G2_13(mesh), G2_23(mesh), G3_11(mesh), G3_22(mesh), G3_33(mesh), G3_12(mesh), G3_13(mesh), G3_23(mesh), G1(mesh), G2(mesh), G3(mesh), ShiftTorsion(mesh), - IntShiftTorsion(mesh), localmesh(mesh), location(CELL_CENTRE) { + IntShiftTorsion(mesh), localmesh(mesh), localoptions(options), + location(CELL_CENTRE) { if (options == nullptr) { options = Options::getRoot()->getSection("mesh"); @@ -444,7 +445,7 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) transform.get()); if (mesh->periodicX) { - communicate(dx); + mesh->communicate_no_slices(dx); } dy = interpolateAndExtrapolate(dy, location, extrapolate_x, extrapolate_y, false, @@ -507,13 +508,13 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) output_warn.write("Not all covariant components of metric tensor found. " "Calculating all from the contravariant tensor\n"); /// Calculate contravariant metric components if not found - if (calcCovariant("RGN_NOCORNERS")) { + if (calcCovariant("RGN_NOCORNERS") != 0) { throw BoutException("Error in calcCovariant call"); } } } else { /// Calculate contravariant metric components if not found - if (calcCovariant("RGN_NOCORNERS")) { + if (calcCovariant("RGN_NOCORNERS") != 0) { throw BoutException("Error in calcCovariant call"); } } @@ -553,7 +554,7 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) // Compare calculated and loaded values output_warn.write("\tMaximum difference in J is {:e}\n", max(abs(J - Jcalc))); - communicate(J); + mesh->communicate_no_slices(J); // Re-evaluate Bxy using new J Bxy = sqrt(g_22) / J; @@ -603,6 +604,9 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) // IntShiftTorsion will not be used, but set to zero to avoid uninitialized field IntShiftTorsion = 0.; } + + // Allow transform to fix things up + transform->loadParallelMetrics(this); } Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, @@ -616,7 +620,7 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, G1_13(mesh), G1_23(mesh), G2_11(mesh), G2_22(mesh), G2_33(mesh), G2_12(mesh), G2_13(mesh), G2_23(mesh), G3_11(mesh), G3_22(mesh), G3_33(mesh), G3_12(mesh), G3_13(mesh), G3_23(mesh), G1(mesh), G2(mesh), G3(mesh), ShiftTorsion(mesh), - IntShiftTorsion(mesh), localmesh(mesh), location(loc) { + IntShiftTorsion(mesh), localmesh(mesh), localoptions(options), location(loc) { std::string suffix = getLocationSuffix(location); @@ -660,7 +664,7 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, extrapolate_y, false, transform.get()); if (mesh->periodicX) { - communicate(dx); + mesh->communicate_no_slices(dx); } getAtLocAndFillGuards(mesh, dy, "dy", suffix, location, 1.0, extrapolate_x, @@ -782,7 +786,6 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, } else { Bxy = interpolateAndExtrapolate(Bxy, location, extrapolate_x, extrapolate_y, false, transform.get()); - output_warn.write("\tMaximum difference in Bxy is %e\n", max(abs(Bxy - Bcalc))); } @@ -891,6 +894,8 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, true, true, false, transform.get()); } } + // Allow transform to fix things up + transform->loadParallelMetrics(this); } void Coordinates::outputVars(Options& output_options) { @@ -943,9 +948,9 @@ const Field2D& Coordinates::zlength() const { int Coordinates::geometry(bool recalculate_staggered, bool force_interpolate_from_centre) { - TRACE("Coordinates::geometry"); - communicate(dx, dy, dz, g11, g22, g33, g12, g13, g23, g_11, g_22, g_33, g_12, g_13, - g_23, J, Bxy); + + localmesh->communicate_no_slices(dx, dy, dz, g11, g22, g33, g12, g13, g23, g_11, g_22, + g_33, g_12, g_13, g_23, J, Bxy); output_progress.write("Calculating differential geometry terms\n"); @@ -969,114 +974,119 @@ int Coordinates::geometry(bool recalculate_staggered, // Note: This calculation is completely general: metric // tensor can be 2D or 3D. For 2D, all DDZ terms are zero - G1_11 = 0.5 * g11 * DDX(g_11) + g12 * (DDX(g_12) - 0.5 * DDY(g_11)) - + g13 * (DDX(g_13) - 0.5 * DDZ(g_11)); - G1_22 = g11 * (DDY(g_12) - 0.5 * DDX(g_22)) + 0.5 * g12 * DDY(g_22) - + g13 * (DDY(g_23) - 0.5 * DDZ(g_22)); - G1_33 = g11 * (DDZ(g_13) - 0.5 * DDX(g_33)) + g12 * (DDZ(g_23) - 0.5 * DDY(g_33)) - + 0.5 * g13 * DDZ(g_33); - G1_12 = 0.5 * g11 * DDY(g_11) + 0.5 * g12 * DDX(g_22) - + 0.5 * g13 * (DDY(g_13) + DDX(g_23) - DDZ(g_12)); - G1_13 = 0.5 * g11 * DDZ(g_11) + 0.5 * g12 * (DDZ(g_12) + DDX(g_23) - DDY(g_13)) - + 0.5 * g13 * DDX(g_33); - G1_23 = 0.5 * g11 * (DDZ(g_12) + DDY(g_13) - DDX(g_23)) - + 0.5 * g12 * (DDZ(g_22) + DDY(g_23) - DDY(g_23)) - // + 0.5 *g13*(DDZ(g_32) + DDY(g_33) - DDZ(g_23)); - // which equals - + 0.5 * g13 * DDY(g_33); - - G2_11 = 0.5 * g12 * DDX(g_11) + g22 * (DDX(g_12) - 0.5 * DDY(g_11)) - + g23 * (DDX(g_13) - 0.5 * DDZ(g_11)); - G2_22 = g12 * (DDY(g_12) - 0.5 * DDX(g_22)) + 0.5 * g22 * DDY(g_22) - + g23 * (DDY(g23) - 0.5 * DDZ(g_22)); - G2_33 = g12 * (DDZ(g_13) - 0.5 * DDX(g_33)) + g22 * (DDZ(g_23) - 0.5 * DDY(g_33)) - + 0.5 * g23 * DDZ(g_33); - G2_12 = 0.5 * g12 * DDY(g_11) + 0.5 * g22 * DDX(g_22) - + 0.5 * g23 * (DDY(g_13) + DDX(g_23) - DDZ(g_12)); - G2_13 = - // 0.5 *g21*(DDZ(g_11) + DDX(g_13) - DDX(g_13)) - // which equals - 0.5 * g12 * (DDZ(g_11) + DDX(g_13) - DDX(g_13)) - // + 0.5 *g22*(DDZ(g_21) + DDX(g_23) - DDY(g_13)) - // which equals - + 0.5 * g22 * (DDZ(g_12) + DDX(g_23) - DDY(g_13)) - // + 0.5 *g23*(DDZ(g_31) + DDX(g_33) - DDZ(g_13)); - // which equals - + 0.5 * g23 * DDX(g_33); - G2_23 = 0.5 * g12 * (DDZ(g_12) + DDY(g_13) - DDX(g_23)) + 0.5 * g22 * DDZ(g_22) - + 0.5 * g23 * DDY(g_33); - - G3_11 = 0.5 * g13 * DDX(g_11) + g23 * (DDX(g_12) - 0.5 * DDY(g_11)) - + g33 * (DDX(g_13) - 0.5 * DDZ(g_11)); - G3_22 = g13 * (DDY(g_12) - 0.5 * DDX(g_22)) + 0.5 * g23 * DDY(g_22) - + g33 * (DDY(g_23) - 0.5 * DDZ(g_22)); - G3_33 = g13 * (DDZ(g_13) - 0.5 * DDX(g_33)) + g23 * (DDZ(g_23) - 0.5 * DDY(g_33)) - + 0.5 * g33 * DDZ(g_33); - G3_12 = - // 0.5 *g31*(DDY(g_11) + DDX(g_12) - DDX(g_12)) - // which equals to - 0.5 * g13 * DDY(g_11) - // + 0.5 *g32*(DDY(g_21) + DDX(g_22) - DDY(g_12)) - // which equals to - + 0.5 * g23 * DDX(g_22) - //+ 0.5 *g33*(DDY(g_31) + DDX(g_32) - DDZ(g_12)); - // which equals to - + 0.5 * g33 * (DDY(g_13) + DDX(g_23) - DDZ(g_12)); - G3_13 = 0.5 * g13 * DDZ(g_11) + 0.5 * g23 * (DDZ(g_12) + DDX(g_23) - DDY(g_13)) - + 0.5 * g33 * DDX(g_33); - G3_23 = 0.5 * g13 * (DDZ(g_12) + DDY(g_13) - DDX(g_23)) + 0.5 * g23 * DDZ(g_22) - + 0.5 * g33 * DDY(g_33); - - auto tmp = J * g12; - communicate(tmp); - G1 = (DDX(J * g11) + DDY(tmp) + DDZ(J * g13)) / J; - tmp = J * g22; - communicate(tmp); - G2 = (DDX(J * g12) + DDY(tmp) + DDZ(J * g23)) / J; - tmp = J * g23; - communicate(tmp); - G3 = (DDX(J * g13) + DDY(tmp) + DDZ(J * g33)) / J; - - // Communicate christoffel symbol terms - output_progress.write("\tCommunicating connection terms\n"); - - communicate(G1_11, G1_22, G1_33, G1_12, G1_13, G1_23, G2_11, G2_22, G2_33, G2_12, G2_13, - G2_23, G3_11, G3_22, G3_33, G3_12, G3_13, G3_23, G1, G2, G3); - - // Set boundary guard cells of Christoffel symbol terms - // Ideally, when location is staggered, we would set the upper/outer boundary point - // correctly rather than by extrapolating here: e.g. if location==CELL_YLOW and we are - // at the upper y-boundary the x- and z-derivatives at yend+1 at the boundary can be - // calculated because the guard cells are available, while the y-derivative could be - // calculated from the CELL_CENTRE metric components (which have guard cells available - // past the boundary location). This would avoid the problem that the y-boundary on the - // CELL_YLOW grid is at a 'guard cell' location (yend+1). - // However, the above would require lots of special handling, so just extrapolate for - // now. - G1_11 = interpolateAndExtrapolate(G1_11, location, true, true, true, transform.get()); - G1_22 = interpolateAndExtrapolate(G1_22, location, true, true, true, transform.get()); - G1_33 = interpolateAndExtrapolate(G1_33, location, true, true, true, transform.get()); - G1_12 = interpolateAndExtrapolate(G1_12, location, true, true, true, transform.get()); - G1_13 = interpolateAndExtrapolate(G1_13, location, true, true, true, transform.get()); - G1_23 = interpolateAndExtrapolate(G1_23, location, true, true, true, transform.get()); - - G2_11 = interpolateAndExtrapolate(G2_11, location, true, true, true, transform.get()); - G2_22 = interpolateAndExtrapolate(G2_22, location, true, true, true, transform.get()); - G2_33 = interpolateAndExtrapolate(G2_33, location, true, true, true, transform.get()); - G2_12 = interpolateAndExtrapolate(G2_12, location, true, true, true, transform.get()); - G2_13 = interpolateAndExtrapolate(G2_13, location, true, true, true, transform.get()); - G2_23 = interpolateAndExtrapolate(G2_23, location, true, true, true, transform.get()); - - G3_11 = interpolateAndExtrapolate(G3_11, location, true, true, true, transform.get()); - G3_22 = interpolateAndExtrapolate(G3_22, location, true, true, true, transform.get()); - G3_33 = interpolateAndExtrapolate(G3_33, location, true, true, true, transform.get()); - G3_12 = interpolateAndExtrapolate(G3_12, location, true, true, true, transform.get()); - G3_13 = interpolateAndExtrapolate(G3_13, location, true, true, true, transform.get()); - G3_23 = interpolateAndExtrapolate(G3_23, location, true, true, true, transform.get()); - - G1 = interpolateAndExtrapolate(G1, location, true, true, true, transform.get()); - G2 = interpolateAndExtrapolate(G2, location, true, true, true, transform.get()); - G3 = interpolateAndExtrapolate(G3, location, true, true, true, transform.get()); + if (!g11.isFci()) { + G1_11 = 0.5 * g11 * DDX(g_11) + g12 * (DDX(g_12) - 0.5 * DDY(g_11)) + + g13 * (DDX(g_13) - 0.5 * DDZ(g_11)); + G1_22 = g11 * (DDY(g_12) - 0.5 * DDX(g_22)) + 0.5 * g12 * DDY(g_22) + + g13 * (DDY(g_23) - 0.5 * DDZ(g_22)); + G1_33 = g11 * (DDZ(g_13) - 0.5 * DDX(g_33)) + g12 * (DDZ(g_23) - 0.5 * DDY(g_33)) + + 0.5 * g13 * DDZ(g_33); + G1_12 = 0.5 * g11 * DDY(g_11) + 0.5 * g12 * DDX(g_22) + + 0.5 * g13 * (DDY(g_13) + DDX(g_23) - DDZ(g_12)); + G1_13 = 0.5 * g11 * DDZ(g_11) + 0.5 * g12 * (DDZ(g_12) + DDX(g_23) - DDY(g_13)) + + 0.5 * g13 * DDX(g_33); + G1_23 = 0.5 * g11 * (DDZ(g_12) + DDY(g_13) - DDX(g_23)) + + 0.5 * g12 * (DDZ(g_22) + DDY(g_23) - DDY(g_23)) + // + 0.5 *g13*(DDZ(g_32) + DDY(g_33) - DDZ(g_23)); + // which equals + + 0.5 * g13 * DDY(g_33); + + G2_11 = 0.5 * g12 * DDX(g_11) + g22 * (DDX(g_12) - 0.5 * DDY(g_11)) + + g23 * (DDX(g_13) - 0.5 * DDZ(g_11)); + G2_22 = g12 * (DDY(g_12) - 0.5 * DDX(g_22)) + 0.5 * g22 * DDY(g_22) + + g23 * (DDY(g23) - 0.5 * DDZ(g_22)); + G2_33 = g12 * (DDZ(g_13) - 0.5 * DDX(g_33)) + g22 * (DDZ(g_23) - 0.5 * DDY(g_33)) + + 0.5 * g23 * DDZ(g_33); + G2_12 = 0.5 * g12 * DDY(g_11) + 0.5 * g22 * DDX(g_22) + + 0.5 * g23 * (DDY(g_13) + DDX(g_23) - DDZ(g_12)); + G2_13 = + // 0.5 *g21*(DDZ(g_11) + DDX(g_13) - DDX(g_13)) + // which equals + 0.5 * g12 * (DDZ(g_11) + DDX(g_13) - DDX(g_13)) + // + 0.5 *g22*(DDZ(g_21) + DDX(g_23) - DDY(g_13)) + // which equals + + 0.5 * g22 * (DDZ(g_12) + DDX(g_23) - DDY(g_13)) + // + 0.5 *g23*(DDZ(g_31) + DDX(g_33) - DDZ(g_13)); + // which equals + + 0.5 * g23 * DDX(g_33); + G2_23 = 0.5 * g12 * (DDZ(g_12) + DDY(g_13) - DDX(g_23)) + 0.5 * g22 * DDZ(g_22) + + 0.5 * g23 * DDY(g_33); + + G3_11 = 0.5 * g13 * DDX(g_11) + g23 * (DDX(g_12) - 0.5 * DDY(g_11)) + + g33 * (DDX(g_13) - 0.5 * DDZ(g_11)); + G3_22 = g13 * (DDY(g_12) - 0.5 * DDX(g_22)) + 0.5 * g23 * DDY(g_22) + + g33 * (DDY(g_23) - 0.5 * DDZ(g_22)); + G3_33 = g13 * (DDZ(g_13) - 0.5 * DDX(g_33)) + g23 * (DDZ(g_23) - 0.5 * DDY(g_33)) + + 0.5 * g33 * DDZ(g_33); + G3_12 = + // 0.5 *g31*(DDY(g_11) + DDX(g_12) - DDX(g_12)) + // which equals to + 0.5 * g13 * DDY(g_11) + // + 0.5 *g32*(DDY(g_21) + DDX(g_22) - DDY(g_12)) + // which equals to + + 0.5 * g23 * DDX(g_22) + //+ 0.5 *g33*(DDY(g_31) + DDX(g_32) - DDZ(g_12)); + // which equals to + + 0.5 * g33 * (DDY(g_13) + DDX(g_23) - DDZ(g_12)); + G3_13 = 0.5 * g13 * DDZ(g_11) + 0.5 * g23 * (DDZ(g_12) + DDX(g_23) - DDY(g_13)) + + 0.5 * g33 * DDX(g_33); + G3_23 = 0.5 * g13 * (DDZ(g_12) + DDY(g_13) - DDX(g_23)) + 0.5 * g23 * DDZ(g_22) + + 0.5 * g33 * DDY(g_33); + + G1 = (DDX(J * g11) + DDY(J.asField3DParallel() * g12) + DDZ(J * g13)) / J; + G2 = (DDX(J * g12) + DDY(J.asField3DParallel() * g22) + DDZ(J * g23)) / J; + G3 = (DDX(J * g13) + DDY(J.asField3DParallel() * g23) + DDZ(J * g33)) / J; + + // Communicate christoffel symbol terms + output_progress.write("\tCommunicating connection terms\n"); + + localmesh->communicate_no_slices(G1_11, G1_22, G1_33, G1_12, G1_13, G1_23, G2_11, + G2_22, G2_33, G2_12, G2_13, G2_23, G3_11, G3_22, + G3_33, G3_12, G3_13, G3_23, G1, G2, G3); + + // Set boundary guard cells of Christoffel symbol terms + // Ideally, when location is staggered, we would set the upper/outer boundary point + // correctly rather than by extrapolating here: e.g. if location==CELL_YLOW and we are + // at the upper y-boundary the x- and z-derivatives at yend+1 at the boundary can be + // calculated because the guard cells are available, while the y-derivative could be + // calculated from the CELL_CENTRE metric components (which have guard cells available + // past the boundary location). This would avoid the problem that the y-boundary on the + // CELL_YLOW grid is at a 'guard cell' location (yend+1). + // However, the above would require lots of special handling, so just extrapolate for + // now. + G1_11 = interpolateAndExtrapolate(G1_11, location, true, true, true, transform.get()); + G1_22 = interpolateAndExtrapolate(G1_22, location, true, true, true, transform.get()); + G1_33 = interpolateAndExtrapolate(G1_33, location, true, true, true, transform.get()); + G1_12 = interpolateAndExtrapolate(G1_12, location, true, true, true, transform.get()); + G1_13 = interpolateAndExtrapolate(G1_13, location, true, true, true, transform.get()); + G1_23 = interpolateAndExtrapolate(G1_23, location, true, true, true, transform.get()); + + G2_11 = interpolateAndExtrapolate(G2_11, location, true, true, true, transform.get()); + G2_22 = interpolateAndExtrapolate(G2_22, location, true, true, true, transform.get()); + G2_33 = interpolateAndExtrapolate(G2_33, location, true, true, true, transform.get()); + G2_12 = interpolateAndExtrapolate(G2_12, location, true, true, true, transform.get()); + G2_13 = interpolateAndExtrapolate(G2_13, location, true, true, true, transform.get()); + G2_23 = interpolateAndExtrapolate(G2_23, location, true, true, true, transform.get()); + + G3_11 = interpolateAndExtrapolate(G3_11, location, true, true, true, transform.get()); + G3_22 = interpolateAndExtrapolate(G3_22, location, true, true, true, transform.get()); + G3_33 = interpolateAndExtrapolate(G3_33, location, true, true, true, transform.get()); + G3_12 = interpolateAndExtrapolate(G3_12, location, true, true, true, transform.get()); + G3_13 = interpolateAndExtrapolate(G3_13, location, true, true, true, transform.get()); + G3_23 = interpolateAndExtrapolate(G3_23, location, true, true, true, transform.get()); + + G1 = interpolateAndExtrapolate(G1, location, true, true, true, transform.get()); + G2 = interpolateAndExtrapolate(G2, location, true, true, true, transform.get()); + G3 = interpolateAndExtrapolate(G3, location, true, true, true, transform.get()); + } else { + G1_11 = G1_22 = G1_33 = G1_12 = G1_13 = G1_23 = + + G2_11 = G2_22 = G2_33 = G2_12 = G2_13 = G2_23 = + + G3_11 = G3_22 = G3_33 = G3_12 = G3_13 = G3_23 = + + G1 = G2 = G3 = BoutNaN; + } ////////////////////////////////////////////////////// /// Non-uniform meshes. Need to use DDX, DDY @@ -1096,9 +1106,9 @@ int Coordinates::geometry(bool recalculate_staggered, if (localmesh->get(d2x, "d2x" + suffix, 0.0, false, location)) { output_warn.write( "\tWARNING: differencing quantity 'd2x' not found. Calculating from dx\n"); - d1_dx = bout::derivatives::index::DDX(1. / dx); // d/di(1/dx) + d1_dx = bout::derivatives::index::DDX(FieldMetric{1. / dx}); // d/di(1/dx) - communicate(d1_dx); + localmesh->communicate_no_slices(d1_dx); d1_dx = interpolateAndExtrapolate(d1_dx, location, true, true, true, transform.get()); } else { @@ -1113,9 +1123,9 @@ int Coordinates::geometry(bool recalculate_staggered, if (localmesh->get(d2y, "d2y" + suffix, 0.0, false, location)) { output_warn.write( "\tWARNING: differencing quantity 'd2y' not found. Calculating from dy\n"); - d1_dy = DDY(1. / dy); // d/di(1/dy) + d1_dy = DDY(1. / dy.asField3DParallel()); // d/di(1/dy) - communicate(d1_dy); + localmesh->communicate_no_slices(d1_dy); d1_dy = interpolateAndExtrapolate(d1_dy, location, true, true, true, transform.get()); } else { @@ -1131,8 +1141,8 @@ int Coordinates::geometry(bool recalculate_staggered, if (localmesh->get(d2z, "d2z" + suffix, 0.0, false)) { output_warn.write( "\tWARNING: differencing quantity 'd2z' not found. Calculating from dz\n"); - d1_dz = bout::derivatives::index::DDZ(1. / dz); - communicate(d1_dz); + d1_dz = bout::derivatives::index::DDZ(FieldMetric{1. / dz}); + localmesh->communicate_no_slices(d1_dz); d1_dz = interpolateAndExtrapolate(d1_dz, location, true, true, true, transform.get()); } else { @@ -1150,9 +1160,9 @@ int Coordinates::geometry(bool recalculate_staggered, if (localmesh->get(d2x, "d2x", 0.0, false)) { output_warn.write( "\tWARNING: differencing quantity 'd2x' not found. Calculating from dx\n"); - d1_dx = bout::derivatives::index::DDX(1. / dx); // d/di(1/dx) + d1_dx = bout::derivatives::index::DDX(FieldMetric{1. / dx}); // d/di(1/dx) - communicate(d1_dx); + localmesh->communicate_no_slices(d1_dx); d1_dx = interpolateAndExtrapolate(d1_dx, location, true, true, true, transform.get()); } else { @@ -1165,9 +1175,9 @@ int Coordinates::geometry(bool recalculate_staggered, if (localmesh->get(d2y, "d2y", 0.0, false)) { output_warn.write( "\tWARNING: differencing quantity 'd2y' not found. Calculating from dy\n"); - d1_dy = DDY(1. / dy); // d/di(1/dy) + d1_dy = DDY(FieldMetric{1. / dy}); // d/di(1/dy) - communicate(d1_dy); + localmesh->communicate_no_slices(d1_dy); d1_dy = interpolateAndExtrapolate(d1_dy, location, true, true, true, transform.get()); } else { @@ -1181,9 +1191,9 @@ int Coordinates::geometry(bool recalculate_staggered, if (localmesh->get(d2z, "d2z", 0.0, false)) { output_warn.write( "\tWARNING: differencing quantity 'd2z' not found. Calculating from dz\n"); - d1_dz = bout::derivatives::index::DDZ(1. / dz); + d1_dz = bout::derivatives::index::DDZ(FieldMetric{1. / dz}); - communicate(d1_dz); + localmesh->communicate_no_slices(d1_dz); d1_dz = interpolateAndExtrapolate(d1_dz, location, true, true, true, transform.get()); } else { @@ -1196,23 +1206,23 @@ int Coordinates::geometry(bool recalculate_staggered, d1_dz = 0; #endif } - communicate(d1_dx, d1_dy, d1_dz); + localmesh->communicate_no_slices(d1_dx, d1_dy, d1_dz); if (location == CELL_CENTRE && recalculate_staggered) { // Re-calculate interpolated Coordinates at staggered locations localmesh->recalculateStaggeredCoordinates(); } - // Invalidate and recalculate cached variables + // Invalidate and recalculate cached variables and any accessor zlength_cache.reset(); Grad2_par2_DDY_invSgCache.clear(); invSgCache.reset(); + CoordinatesAccessor::clear(this); return 0; } int Coordinates::calcCovariant(const std::string& region) { - TRACE("Coordinates::calcCovariant"); // Make sure metric elements are allocated g_11.allocate(); @@ -1244,7 +1254,7 @@ int Coordinates::calcCovariant(const std::string& region) { a(0, 2) = a(2, 0) = g13[i]; if (const auto det = bout::invert3x3(a); det.has_value()) { - output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:d}\n", + output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:e}\n", i, det.value()); return 1; } @@ -1275,7 +1285,6 @@ int Coordinates::calcCovariant(const std::string& region) { } int Coordinates::calcContravariant(const std::string& region) { - TRACE("Coordinates::calcContravariant"); // Make sure metric elements are allocated g11.allocate(); @@ -1300,7 +1309,7 @@ int Coordinates::calcContravariant(const std::string& region) { a(0, 2) = a(2, 0) = g_13[i]; if (const auto det = bout::invert3x3(a); det.has_value()) { - output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:d}\n", + output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:e}\n", i, det.value()); return 1; } @@ -1330,14 +1339,14 @@ int Coordinates::calcContravariant(const std::string& region) { } int Coordinates::jacobian() { - TRACE("Coordinates::jacobian"); + // calculate Jacobian using g^-1 = det[g^ij], J = sqrt(g) const bool extrapolate_x = not localmesh->sourceHasXBoundaryGuards(); const bool extrapolate_y = not localmesh->sourceHasYBoundaryGuards(); - auto g = g11 * g22 * g33 + 2.0 * g12 * g13 * g23 - g11 * g23 * g23 - g22 * g13 * g13 - - g33 * g12 * g12; + const FieldMetric g = g11 * g22 * g33 + 2.0 * g12 * g13 * g23 - g11 * g23 * g23 + - g22 * g13 * g13 - g33 * g12 * g12; // Check that g is positive bout::checkPositive(g, "The determinant of g^ij", "RGN_NOBNDRY"); @@ -1366,7 +1375,7 @@ void fixZShiftGuards(Field2D& zShift) { not localmesh->sourceHasYBoundaryGuards(), false); // make sure zShift has been communicated - communicate(zShift); + localmesh->communicate_no_slices(zShift); // Correct guard cells for discontinuity of zShift at poloidal branch cut for (int x = 0; x < localmesh->LocalNx; x++) { @@ -1498,16 +1507,8 @@ Coordinates::FieldMetric Coordinates::DDY(const Field2D& f, CELL_LOC loc, return bout::derivatives::index::DDY(f, loc, method, region) / dy; } -Field3D Coordinates::DDY(const Field3D& f, CELL_LOC outloc, const std::string& method, - const std::string& region) const { -#if BOUT_USE_METRIC_3D - if (!f.hasParallelSlices() and !transform->canToFromFieldAligned()) { - Field3D f_parallel = f; - transform->calcParallelSlices(f_parallel); - f_parallel.applyParallelBoundary("parallel_neumann_o2"); - return bout::derivatives::index::DDY(f_parallel, outloc, method, region); - } -#endif +Field3D Coordinates::DDY(const Field3DParallel& f, CELL_LOC outloc, + const std::string& method, const std::string& region) const { return bout::derivatives::index::DDY(f, outloc, method, region) / dy; }; @@ -1532,16 +1533,16 @@ Field3D Coordinates::DDZ(const Field3D& f, CELL_LOC outloc, const std::string& m Coordinates::FieldMetric Coordinates::Grad_par(const Field2D& var, [[maybe_unused]] CELL_LOC outloc, const std::string& UNUSED(method)) { - TRACE("Coordinates::Grad_par( Field2D )"); + ASSERT1(location == outloc || (outloc == CELL_DEFAULT && location == var.getLocation())); return DDY(var) * invSg(); } -Field3D Coordinates::Grad_par(const Field3D& var, CELL_LOC outloc, +Field3D Coordinates::Grad_par(const Field3DParallel& var, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Grad_par( Field3D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); return ::DDY(var, outloc, method) * invSg(); @@ -1559,8 +1560,8 @@ Coordinates::FieldMetric Coordinates::Vpar_Grad_par(const Field2D& v, const Fiel return VDDY(v, f) * invSg(); } -Field3D Coordinates::Vpar_Grad_par(const Field3D& v, const Field3D& f, CELL_LOC outloc, - const std::string& method) { +Field3D Coordinates::Vpar_Grad_par(const Field3D& v, const Field3DParallel& f, + CELL_LOC outloc, const std::string& method) { ASSERT1(location == outloc || outloc == CELL_DEFAULT); return VDDY(v, f, outloc, method) * invSg(); @@ -1571,39 +1572,26 @@ Field3D Coordinates::Vpar_Grad_par(const Field3D& v, const Field3D& f, CELL_LOC Coordinates::FieldMetric Coordinates::Div_par(const Field2D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Div_par( Field2D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); // Need Bxy at location of f, which might be different from location of this // Coordinates object auto Bxy_floc = f.getCoordinates()->Bxy; - return Bxy * Grad_par(f / Bxy_floc, outloc, method); + return Bxy * Grad_par(FieldMetric{f / Bxy_floc}, outloc, method); } -Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, +Field3D Coordinates::Div_par(const Field3DParallel& f, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Div_par( Field3D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); // Need Bxy at location of f, which might be different from location of this // Coordinates object - auto Bxy_floc = f.getCoordinates()->Bxy; - - if (!f.hasParallelSlices()) { - // No yup/ydown fields. The Grad_par operator will - // shift to field aligned coordinates - return Bxy * Grad_par(f / Bxy_floc, outloc, method); - } + const auto& Bxy_floc = f.getCoordinates()->Bxy; - // Need to modify yup and ydown fields - Field3D f_B = f / Bxy_floc; - f_B.splitParallelSlices(); - for (int i = 0; i < f.getMesh()->ystart; ++i) { - f_B.yup(i) = f.yup(i) / Bxy_floc.yup(i); - f_B.ydown(i) = f.ydown(i) / Bxy_floc.ydown(i); - } - return Bxy * Grad_par(f_B, outloc, method); + return Bxy * Grad_par(f / Bxy_floc, outloc, method); } ///////////////////////////////////////////////////////// @@ -1612,7 +1600,7 @@ Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, Coordinates::FieldMetric Coordinates::Grad2_par2(const Field2D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Grad2_par2( Field2D )"); + ASSERT1(location == outloc || (outloc == CELL_DEFAULT && location == f.getLocation())); auto result = Grad2_par2_DDY_invSg(outloc, method) * DDY(f, outloc, method) @@ -1621,9 +1609,9 @@ Coordinates::FieldMetric Coordinates::Grad2_par2(const Field2D& f, CELL_LOC outl return result; } -Field3D Coordinates::Grad2_par2(const Field3D& f, CELL_LOC outloc, +Field3D Coordinates::Grad2_par2(const Field3DParallel& f, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Grad2_par2( Field3D )"); + if (outloc == CELL_DEFAULT) { outloc = f.getLocation(); } @@ -1647,7 +1635,7 @@ Field3D Coordinates::Grad2_par2(const Field3D& f, CELL_LOC outloc, Coordinates::FieldMetric Coordinates::Delp2(const Field2D& f, CELL_LOC outloc, bool UNUSED(useFFT)) { - TRACE("Coordinates::Delp2( Field2D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); auto result = G1 * DDX(f, outloc) + g11 * D2DX2(f, outloc); @@ -1656,7 +1644,6 @@ Coordinates::FieldMetric Coordinates::Delp2(const Field2D& f, CELL_LOC outloc, } Field3D Coordinates::Delp2(const Field3D& f, CELL_LOC outloc, bool useFFT) { - TRACE("Coordinates::Delp2( Field3D )"); if (outloc == CELL_DEFAULT) { outloc = f.getLocation(); @@ -1673,7 +1660,7 @@ Field3D Coordinates::Delp2(const Field3D& f, CELL_LOC outloc, bool useFFT) { Field3D result{emptyFrom(f).setLocation(outloc)}; - if (useFFT and not bout::build::use_metric_3d) { + if (useFFT and not bout::build::use_metric_3d and localmesh->getNZPE() == 1) { int ncz = localmesh->LocalNz; // Allocate memory @@ -1722,7 +1709,6 @@ Field3D Coordinates::Delp2(const Field3D& f, CELL_LOC outloc, bool useFFT) { } FieldPerp Coordinates::Delp2(const FieldPerp& f, CELL_LOC outloc, bool useFFT) { - TRACE("Coordinates::Delp2( FieldPerp )"); if (outloc == CELL_DEFAULT) { outloc = f.getLocation(); @@ -1742,7 +1728,7 @@ FieldPerp Coordinates::Delp2(const FieldPerp& f, CELL_LOC outloc, bool useFFT) { int jy = f.getIndex(); result.setIndex(jy); - if (useFFT) { + if (useFFT and localmesh->getNZPE() == 1) { int ncz = localmesh->LocalNz; // Allocate memory @@ -1786,12 +1772,14 @@ FieldPerp Coordinates::Delp2(const FieldPerp& f, CELL_LOC outloc, bool useFFT) { Coordinates::FieldMetric Coordinates::Laplace_par(const Field2D& f, CELL_LOC outloc) { ASSERT1(location == outloc || outloc == CELL_DEFAULT); - return D2DY2(f, outloc) / g_22 + DDY(J / g_22, outloc) * DDY(f, outloc) / J; + return D2DY2(f, outloc) / g_22 + + DDY(FieldMetric{J / g_22}, outloc) * DDY(f, outloc) / J; } -Field3D Coordinates::Laplace_par(const Field3D& f, CELL_LOC outloc) { +Field3D Coordinates::Laplace_par(const Field3DParallel& f, CELL_LOC outloc) { ASSERT1(location == outloc || outloc == CELL_DEFAULT); - return D2DY2(f, outloc) / g_22 + DDY(J / g_22, outloc) * ::DDY(f, outloc) / J; + return D2DY2(f, outloc) / g_22 + + DDY(J.asField3DParallel() / g_22, outloc) * ::DDY(f, outloc) / J; } // Full Laplacian operator on scalar field @@ -1799,7 +1787,7 @@ Field3D Coordinates::Laplace_par(const Field3D& f, CELL_LOC outloc) { Coordinates::FieldMetric Coordinates::Laplace(const Field2D& f, CELL_LOC outloc, const std::string& dfdy_boundary_conditions, const std::string& dfdy_dy_region) { - TRACE("Coordinates::Laplace( Field2D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); auto result = G1 * DDX(f, outloc) + G2 * DDY(f, outloc) + g11 * D2DX2(f, outloc) @@ -1811,10 +1799,10 @@ Coordinates::FieldMetric Coordinates::Laplace(const Field2D& f, CELL_LOC outloc, return result; } -Field3D Coordinates::Laplace(const Field3D& f, CELL_LOC outloc, +Field3D Coordinates::Laplace(const Field3DParallel& f, CELL_LOC outloc, const std::string& dfdy_boundary_conditions, const std::string& dfdy_dy_region) { - TRACE("Coordinates::Laplace( Field3D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); Field3D result = G1 * ::DDX(f, outloc) + G2 * ::DDY(f, outloc) + G3 * ::DDZ(f, outloc) @@ -1833,7 +1821,7 @@ Field3D Coordinates::Laplace(const Field3D& f, CELL_LOC outloc, // solver Field2D Coordinates::Laplace_perpXY([[maybe_unused]] const Field2D& A, [[maybe_unused]] const Field2D& f) { - TRACE("Coordinates::Laplace_perpXY( Field2D )"); + #if not(BOUT_USE_METRIC_3D) Field2D result; result.allocate(); @@ -1893,7 +1881,7 @@ Field2D Coordinates::Laplace_perpXY([[maybe_unused]] const Field2D& A, const Coordinates::FieldMetric& Coordinates::invSg() const { if (invSgCache == nullptr) { - auto ptr = std::make_unique(); + auto ptr = std::make_unique(); (*ptr) = 1.0 / sqrt(g_22); invSgCache = std::move(ptr); } @@ -1913,7 +1901,7 @@ Coordinates::Grad2_par2_DDY_invSg(CELL_LOC outloc, const std::string& method) co invSgCache->applyParallelBoundary("parallel_neumann_o2"); // cache - auto ptr = std::make_unique(); + auto ptr = std::make_unique(); *ptr = DDY(*invSgCache, outloc, method) * invSg(); Grad2_par2_DDY_invSgCache[method] = std::move(ptr); return *Grad2_par2_DDY_invSgCache[method]; @@ -2022,3 +2010,125 @@ void Coordinates::checkContravariant() { } } } + +const Coordinates::FieldMetric& Coordinates::g_22_ylow() const { + if (_g_22_ylow.has_value()) { + return *_g_22_ylow; + } + _g_22_ylow.emplace(emptyFrom(g_22)); + //_g_22_ylow->setLocation(CELL_YLOW); + auto* mesh = Bxy.getMesh(); + if (Bxy.isFci()) { + if (mesh->get(_g_22_ylow.value(), "g_22_cell_ylow", 0.0, false) != 0) { + throw BoutException("The grid file does not contain `g_22_cell_ylow`."); + } + } else { + ASSERT0(mesh->ystart > 0); + BOUT_FOR(i, g_22.getRegion("RGN_NOY")) { + _g_22_ylow.value()[i] = SQ(0.5 * (std::sqrt(g_22[i]) + std::sqrt(g_22[i.ym()]))); + } + } + return g_22_ylow(); +} + +const Coordinates::FieldMetric& Coordinates::g_22_yhigh() const { + if (_g_22_yhigh.has_value()) { + return *_g_22_yhigh; + } + _g_22_yhigh.emplace(emptyFrom(g_22)); + auto* mesh = Bxy.getMesh(); + if (Bxy.isFci()) { + if (mesh->get(_g_22_yhigh.value(), "g_22_cell_yhigh", 0.0, false) != 0) { + throw BoutException("The grid file does not contain `g_22_cell_yhigh`."); + } + } else { + ASSERT0(mesh->ystart > 0); + BOUT_FOR(i, g_22.getRegion("RGN_NOY")) { + _g_22_yhigh.value()[i] = SQ(0.5 * (std::sqrt(g_22[i]) + std::sqrt(g_22[i.yp()]))); + } + } + return g_22_yhigh(); +} + +void Coordinates::_compute_cell_area_x() const { + const FieldMetric area_centre = sqrt(g_22 * g_33 - SQ(g_23)) * dy * dz; + _cell_area_xlow.emplace(emptyFrom(area_centre)); + _cell_area_xhigh.emplace(emptyFrom(area_centre)); + // We cannot setLocation, as that would trigger the computation of staggered + // metrics. + auto* mesh = Bxy.getMesh(); + ASSERT0(mesh->xstart > 0); + BOUT_FOR(i, area_centre.getRegion("RGN_NOX")) { + (*_cell_area_xlow)[i] = 0.5 * (area_centre[i] + area_centre[i.xm()]); + (*_cell_area_xhigh)[i] = 0.5 * (area_centre[i] + area_centre[i.xp()]); + } +} + +void Coordinates::_compute_cell_area_y() const { + auto* mesh = Bxy.getMesh(); + if (g_11.isFci()) { + const FieldMetric jxz_centre = sqrt(g_11 * g_33 - SQ(g_13)); + auto jxz_ylow = emptyFrom(jxz_centre); + auto jxz_yhigh = emptyFrom(jxz_centre); + + auto By_c = emptyFrom(jxz_centre); + auto By_h = emptyFrom(jxz_yhigh); + auto By_l = emptyFrom(jxz_ylow); + if (mesh->get(By_c, "By", 0.0, false, CELL_CENTRE) != 0) { + throw BoutException("The grid file does not contain `By`."); + } + if (mesh->get(By_l, "By_cell_ylow", 0.0, false) != 0) { + throw BoutException("The grid file does not contain `By_cell_ylow`."); + } + if (mesh->get(By_h, "By_cell_yhigh", 0.0, false) != 0) { + throw BoutException("The grid file does not contain `By_cell_yhigh`."); + } + BOUT_FOR(i, By_c.getRegion("RGN_NOY")) { + jxz_ylow[i] = By_c[i] / By_l[i] * jxz_centre[i]; + jxz_yhigh[i] = By_c[i] / By_h[i] * jxz_centre[i]; + } + ASSERT3(isUniform(dx, true, "RGN_ALL")); + ASSERT2(isUniform(dx, false, "RGN_ALL")); + ASSERT3(isUniform(dz, true, "RGN_ALL")); + ASSERT2(isUniform(dz, false, "RGN_ALL")); + _cell_area_ylow.emplace(jxz_ylow * dx * dz); + _cell_area_yhigh.emplace(jxz_yhigh * dx * dz); + } else { + // Field aligned + const FieldMetric area_centre = sqrt(g_11 * g_33 - SQ(g_13)) * dx * dz; + _cell_area_ylow.emplace(emptyFrom(area_centre)); + _cell_area_yhigh.emplace(emptyFrom(area_centre)); + // We cannot setLocation, as that would trigger the computation of staggered + // metrics. + BOUT_FOR(i, mesh->getRegion("RGN_ALL")) { + if (i.y() > 0) { + (*_cell_area_ylow)[i] = 0.5 * (area_centre[i] + area_centre[i.ym()]); + } else { + (*_cell_area_ylow)[i] = BoutNaN; + } + if (i.y() < mesh->LocalNy - 1) { + (*_cell_area_yhigh)[i] = 0.5 * (area_centre[i] + area_centre[i.yp()]); + } else { + (*_cell_area_yhigh)[i] = BoutNaN; + } + } + } +} + +void Coordinates::_compute_cell_area_z() const { + const FieldMetric area_centre = sqrt(g_11 * g_22 - SQ(g_12)) * dx * dy; + _cell_area_zlow.emplace(emptyFrom(area_centre)); + _cell_area_zhigh.emplace(emptyFrom(area_centre)); + // We cannot setLocation, as that would trigger the computation of staggered + // metrics. + BOUT_FOR(i, area_centre.getRegion("RGN_NOZ")) { + (*_cell_area_zlow)[i] = 0.5 * (area_centre[i] + area_centre[i.zm()]); + (*_cell_area_zhigh)[i] = 0.5 * (area_centre[i] + area_centre[i.zp()]); + } +} + +void Coordinates::_compute_cell_volume() const { _cell_volume.emplace(J * dx * dy * dz); } + +std::shared_ptr Coordinates::makeYBoundary(YBndryType type) const { + return std::make_shared(type, localoptions, *localmesh); +} diff --git a/src/mesh/coordinates_accessor.cxx b/src/mesh/coordinates_accessor.cxx index aff546c2b0..efc27e9715 100644 --- a/src/mesh/coordinates_accessor.cxx +++ b/src/mesh/coordinates_accessor.cxx @@ -1,5 +1,6 @@ #include "bout/coordinates_accessor.hxx" - +#include "bout/build_defines.hxx" +#include "bout/macro_for_each.hxx" #include "bout/mesh.hxx" #include @@ -40,12 +41,15 @@ CoordinatesAccessor::CoordinatesAccessor(const Coordinates* coords) { // Copy data from Coordinates variable into data array // Uses the symbol to look up the corresponding Offset -#define COPY_STRIPE1(symbol) \ - data[stripe_size * ind.ind + static_cast(Offset::symbol)] = coords->symbol[ind]; +#define COPY_STRIPE1(symbol) \ + if (coords->symbol.isAllocated()) \ + data[stripe_size * ind.ind + static_cast(Offset::symbol)] = coords->symbol[ind]; // Implement copy for each argument -#define COPY_STRIPE(...) \ - { MACRO_FOR_EACH(COPY_STRIPE1, __VA_ARGS__) } +#define COPY_STRIPE(...) \ + { \ + MACRO_FOR_EACH(COPY_STRIPE1, __VA_ARGS__) \ + } // Iterate over all points in the field // Note this could be 2D or 3D, depending on FieldMetric type @@ -54,10 +58,15 @@ CoordinatesAccessor::CoordinatesAccessor(const Coordinates* coords) { COPY_STRIPE(d1_dx, d1_dy, d1_dz); COPY_STRIPE(J); - data[stripe_size * ind.ind + static_cast(Offset::B)] = coords->Bxy[ind]; - data[stripe_size * ind.ind + static_cast(Offset::Byup)] = coords->Bxy.yup()[ind]; - data[stripe_size * ind.ind + static_cast(Offset::Bydown)] = - coords->Bxy.ydown()[ind]; + if (coords->Bxy.isAllocated()) { + data[stripe_size * ind.ind + static_cast(Offset::B)] = coords->Bxy[ind]; + if (coords->Bxy.yup().isAllocated()) + data[stripe_size * ind.ind + static_cast(Offset::Byup)] = + coords->Bxy.yup()[ind]; + if (coords->Bxy.ydown().isAllocated()) + data[stripe_size * ind.ind + static_cast(Offset::Bydown)] = + coords->Bxy.ydown()[ind]; + } COPY_STRIPE(G1, G3); COPY_STRIPE(g11, g12, g13, g22, g23, g33); diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 5625522795..27ad9411c2 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -2,21 +2,23 @@ #include "bout/traits.hxx" #include +#include #include #include #include -#include #include #include #include #include #include + +#include +#include #include GridFile::GridFile(std::string gridfilename) : GridDataSource(true), data(bout::OptionsIO::create(gridfilename)->read()), filename(std::move(gridfilename)) { - TRACE("GridFile constructor"); // Get number of y-boundary guard cells saved in the grid file grid_yguards = data["y_boundary_guards"].withDefault(0); @@ -56,7 +58,7 @@ bool GridFile::hasVar(const std::string& name) { return data.isSet(name); } bool GridFile::get(Mesh* UNUSED(m), std::string& sval, const std::string& name, const std::string& def) { Timer timer("io"); - TRACE("GridFile::get(std::string)"); + const bool success = data.isSet(name); if (not success) { // Override any previously set defaults @@ -89,7 +91,7 @@ bool GridFile::get(Mesh* UNUSED(m), std::string& sval, const std::string& name, */ bool GridFile::get(Mesh* UNUSED(m), int& ival, const std::string& name, int def) { Timer timer("io"); - TRACE("GridFile::get(int)"); + const bool success = data.isSet(name); if (not success) { // Override any previously set defaults @@ -106,7 +108,7 @@ bool GridFile::get(Mesh* UNUSED(m), int& ival, const std::string& name, int def) bool GridFile::get(Mesh* UNUSED(m), BoutReal& rval, const std::string& name, BoutReal def) { Timer timer("io"); - TRACE("GridFile::get(BoutReal)"); + const bool success = data.isSet(name); if (not success) { // Override any previously set defaults @@ -118,7 +120,7 @@ bool GridFile::get(Mesh* UNUSED(m), BoutReal& rval, const std::string& name, /*! * Reads a 2D, 3D or FieldPerp field variable from a file - * + * * Successfully reads Field2D or FieldPerp if the variable in the file is 0-D or 2-D. * Successfully reads Field3D if the variable in the file is 0-D, 2-D or 3-D. */ @@ -139,7 +141,6 @@ bool GridFile::getField(Mesh* m, T& var, const std::string& name, BoutReal def, "templated GridFile::getField only works for Field2D, Field3D or FieldPerp"); Timer timer("io"); - AUTO_TRACE(); if (not data.isSet(name)) { // Variable not found @@ -206,9 +207,6 @@ bool GridFile::getField(Mesh* m, T& var, const std::string& name, BoutReal def, // we pass int ys = m->OffsetY; - // Total number of y-boundary cells in grid file, used for check later. - // Value depends on if we are double-null or not. - int total_grid_yguards = 2 * grid_yguards; if (m->numberOfXPoints > 1) { ASSERT1(m->numberOfXPoints == 2); // Need to check if we are before or after the target in the middle of the @@ -218,9 +216,6 @@ bool GridFile::getField(Mesh* m, T& var, const std::string& name, BoutReal def, // Note: neither ny_inner nor OffsetY include guard cells ys += 2 * grid_yguards; } - - // Add y-boundary guard cells at upper target - total_grid_yguards += 2 * grid_yguards; } // Index offsets into destination @@ -377,7 +372,7 @@ void GridFile::readField(Mesh* m, const std::string& name, int ys, int yd, int n for (int x = xs; x < xs + nx_to_read; ++x) { for (int y = ys; y < ys + ny_to_read; ++y) { - BoutReal const value = full_var(x, y); + const BoutReal value = full_var(x, y); for (int z = 0; z < var.getNz(); z++) { var(x - xs + xd, y - ys + yd, z) = value; } @@ -469,14 +464,31 @@ bool GridFile::get([[maybe_unused]] Mesh* m, [[maybe_unused]] std::vector& [[maybe_unused]] const std::string& name, [[maybe_unused]] int len, [[maybe_unused]] int offset, [[maybe_unused]] GridDataSource::Direction dir) { - TRACE("GridFile::get(vector)"); - return false; + if (not data.isSet(name)) { + return false; + } + + const auto full_var = data[name].as>(); + + // Check size + if (full_var.size() < len + offset) { + throw BoutException("{} has length {}. Expected {} elements + {} offset", name, + full_var.size(), len, offset); + } + + // Ensure that output variable has the correct size + var.resize(len); + + const auto* it = std::begin(full_var); + std::advance(it, offset); + std::copy_n(it, len, std::begin(var)); + + return true; } bool GridFile::get(Mesh* UNUSED(m), std::vector& var, const std::string& name, int len, int offset, GridDataSource::Direction UNUSED(dir)) { - TRACE("GridFile::get(vector)"); if (not data.isSet(name)) { return false; diff --git a/src/mesh/data/gridfromoptions.cxx b/src/mesh/data/gridfromoptions.cxx index 379e279de0..662329c526 100644 --- a/src/mesh/data/gridfromoptions.cxx +++ b/src/mesh/data/gridfromoptions.cxx @@ -146,8 +146,7 @@ bool GridFromOptions::get(Mesh* m, std::vector& var, const std::string } case GridDataSource::Z: { for (int z = 0; z < len; z++) { - pos.set("z", - (TWOPI * (z - m->OffsetZ + offset)) / static_cast(m->LocalNz)); + pos.set("z", TWOPI * m->GlobalZ(z - m->OffsetZ + offset)); var[z] = gen->generate(pos); } break; diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 42fa4d6ca5..3f7aec08bf 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -2,9 +2,9 @@ * Various differential operators defined on BOUT grid * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -26,11 +26,16 @@ #include "bout/build_defines.hxx" #include +#include +#include +#include #include #include #include +#include +#include #include -#include +#include #include #include #include @@ -285,7 +290,8 @@ Field3D Div_par_flux(const Field3D& v, const Field3D& f, CELL_LOC outloc, auto Bxy_floc = f.getCoordinates()->Bxy; if (!f.hasParallelSlices()) { - return metric->Bxy * FDDY(v, f / Bxy_floc, outloc, method) / sqrt(metric->g_22); + Field3D f_B = f / Bxy_floc; + return metric->Bxy * FDDY(v, f_B, outloc, method) / sqrt(metric->g_22); } // Need to modify yup and ydown fields @@ -458,8 +464,6 @@ Field2D Laplace_perpXY(const Field2D& A, const Field2D& f) { Coordinates::FieldMetric b0xGrad_dot_Grad(const Field2D& phi, const Field2D& A, CELL_LOC outloc) { - TRACE("b0xGrad_dot_Grad( Field2D , Field2D )"); - if (outloc == CELL_DEFAULT) { outloc = A.getLocation(); } @@ -489,7 +493,6 @@ Coordinates::FieldMetric b0xGrad_dot_Grad(const Field2D& phi, const Field2D& A, } Field3D b0xGrad_dot_Grad(const Field2D& phi, const Field3D& A, CELL_LOC outloc) { - TRACE("b0xGrad_dot_Grad( Field2D , Field3D )"); if (outloc == CELL_DEFAULT) { outloc = A.getLocation(); @@ -531,7 +534,6 @@ Field3D b0xGrad_dot_Grad(const Field2D& phi, const Field3D& A, CELL_LOC outloc) } Field3D b0xGrad_dot_Grad(const Field3D& p, const Field2D& A, CELL_LOC outloc) { - TRACE("b0xGrad_dot_Grad( Field3D , Field2D )"); if (outloc == CELL_DEFAULT) { outloc = A.getLocation(); @@ -566,7 +568,6 @@ Field3D b0xGrad_dot_Grad(const Field3D& p, const Field2D& A, CELL_LOC outloc) { } Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field3D& A, CELL_LOC outloc) { - TRACE("b0xGrad_dot_Grad( Field3D , Field3D )"); if (outloc == CELL_DEFAULT) { outloc = A.getLocation(); @@ -614,7 +615,6 @@ Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field3D& A, CELL_LOC outloc) Coordinates::FieldMetric bracket(const Field2D& f, const Field2D& g, BRACKET_METHOD method, CELL_LOC outloc, Solver* UNUSED(solver)) { - TRACE("bracket(Field2D, Field2D)"); ASSERT1_FIELDS_COMPATIBLE(f, g); if (outloc == CELL_DEFAULT) { @@ -637,7 +637,6 @@ Coordinates::FieldMetric bracket(const Field2D& f, const Field2D& g, Field3D bracket(const Field3D& f, const Field2D& g, BRACKET_METHOD method, CELL_LOC outloc, Solver* solver) { - TRACE("bracket(Field3D, Field2D)"); ASSERT1_FIELDS_COMPATIBLE(f, g); if (outloc == CELL_DEFAULT) { @@ -771,46 +770,6 @@ Field3D bracket(const Field3D& f, const Field2D& g, BRACKET_METHOD method, break; } - case BRACKET_ARAKAWA_OLD: { -#if not(BOUT_USE_METRIC_3D) - const int ncz = mesh->LocalNz; - BOUT_OMP_PERF(parallel for) - for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { - for (int jy = mesh->ystart; jy <= mesh->yend; jy++) { - const BoutReal partialFactor = 1.0 / (12 * metric->dz(jx, jy)); - const BoutReal spacingFactor = partialFactor / metric->dx(jx, jy); - for (int jz = 0; jz < mesh->LocalNz; jz++) { - const int jzp = jz + 1 < ncz ? jz + 1 : 0; - // Above is alternative to const int jzp = (jz + 1) % ncz; - const int jzm = jz - 1 >= 0 ? jz - 1 : ncz - 1; - // Above is alternative to const int jzmTmp = (jz - 1 + ncz) % ncz; - - // J++ = DDZ(f)*DDX(g) - DDX(f)*DDZ(g) - BoutReal Jpp = - ((f(jx, jy, jzp) - f(jx, jy, jzm)) * (g(jx + 1, jy) - g(jx - 1, jy)) - - (f(jx + 1, jy, jz) - f(jx - 1, jy, jz)) * (g(jx, jy) - g(jx, jy))); - - // J+x - BoutReal Jpx = (g(jx + 1, jy) * (f(jx + 1, jy, jzp) - f(jx + 1, jy, jzm)) - - g(jx - 1, jy) * (f(jx - 1, jy, jzp) - f(jx - 1, jy, jzm)) - - g(jx, jy) * (f(jx + 1, jy, jzp) - f(jx - 1, jy, jzp)) - + g(jx, jy) * (f(jx + 1, jy, jzm) - f(jx - 1, jy, jzm))); - - // Jx+ - BoutReal Jxp = (g(jx + 1, jy) * (f(jx, jy, jzp) - f(jx + 1, jy, jz)) - - g(jx - 1, jy) * (f(jx - 1, jy, jz) - f(jx, jy, jzm)) - - g(jx - 1, jy) * (f(jx, jy, jzp) - f(jx - 1, jy, jz)) - + g(jx + 1, jy) * (f(jx + 1, jy, jz) - f(jx, jy, jzm))); - - result(jx, jy, jz) = (Jpp + Jpx + Jxp) * spacingFactor; - } - } - } -#else - throw BoutException("BRACKET_ARAKAWA_OLD not valid with 3D metrics yet."); -#endif - break; - } case BRACKET_SIMPLE: { // Use a subset of terms for comparison to BOUT-06 result = VDDX(DDZ(f, outloc), g, outloc); @@ -826,7 +785,6 @@ Field3D bracket(const Field3D& f, const Field2D& g, BRACKET_METHOD method, Field3D bracket(const Field2D& f, const Field3D& g, BRACKET_METHOD method, CELL_LOC outloc, Solver* solver) { - TRACE("bracket(Field2D, Field3D)"); ASSERT1_FIELDS_COMPATIBLE(f, g); if (outloc == CELL_DEFAULT) { @@ -848,7 +806,7 @@ Field3D bracket(const Field2D& f, const Field3D& g, BRACKET_METHOD method, break; case BRACKET_SIMPLE: { // Use a subset of terms for comparison to BOUT-06 - result = VDDZ(-DDX(f, outloc), g, outloc); + result = VDDZ(Field3D{-DDX(f, outloc)}, g, outloc); break; } default: { @@ -863,8 +821,6 @@ Field3D bracket(const Field2D& f, const Field3D& g, BRACKET_METHOD method, Field3D bracket(const Field3D& f, const Field3D& g, BRACKET_METHOD method, CELL_LOC outloc, [[maybe_unused]] Solver* solver) { - TRACE("Field3D, Field3D"); - ASSERT1_FIELDS_COMPATIBLE(f, g); if (outloc == CELL_DEFAULT) { outloc = g.getLocation(); @@ -904,7 +860,7 @@ Field3D bracket(const Field3D& f, const Field3D& g, BRACKET_METHOD method, int ncz = mesh->LocalNz; for (int y = mesh->ystart; y <= mesh->yend; y++) { for (int x = 1; x <= mesh->LocalNx - 2; x++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int zm = (z - 1 + ncz) % ncz; int zp = (z + 1) % ncz; @@ -1090,63 +1046,6 @@ Field3D bracket(const Field3D& f, const Field3D& g, BRACKET_METHOD method, } break; } - case BRACKET_ARAKAWA_OLD: { - // Arakawa scheme for perpendicular flow - - const int ncz = mesh->LocalNz; - - // We need to discard const qualifier in order to manipulate - // storage array directly - Field3D f_temp = f; - Field3D g_temp = g; - - BOUT_OMP_PERF(parallel for) - for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { - for (int jy = mesh->ystart; jy <= mesh->yend; jy++) { -#if not(BOUT_USE_METRIC_3D) - const BoutReal spacingFactor = - 1.0 / (12 * metric->dz(jx, jy) * metric->dx(jx, jy)); -#endif - const BoutReal* Fxm = f_temp(jx - 1, jy); - const BoutReal* Fx = f_temp(jx, jy); - const BoutReal* Fxp = f_temp(jx + 1, jy); - const BoutReal* Gxm = g_temp(jx - 1, jy); - const BoutReal* Gx = g_temp(jx, jy); - const BoutReal* Gxp = g_temp(jx + 1, jy); - for (int jz = 0; jz < mesh->LocalNz; jz++) { -#if BOUT_USE_METRIC_3D - const BoutReal spacingFactor = - 1.0 / (12 * metric->dz(jx, jy, jz) * metric->dx(jx, jy, jz)); -#endif - const int jzp = jz + 1 < ncz ? jz + 1 : 0; - // Above is alternative to const int jzp = (jz + 1) % ncz; - const int jzm = jz - 1 >= 0 ? jz - 1 : ncz - 1; - // Above is alternative to const int jzm = (jz - 1 + ncz) % ncz; - - // J++ = DDZ(f)*DDX(g) - DDX(f)*DDZ(g) - // NOLINTNEXTLINE - BoutReal Jpp = ((Fx[jzp] - Fx[jzm]) * (Gxp[jz] - Gxm[jz]) - - (Fxp[jz] - Fxm[jz]) * (Gx[jzp] - Gx[jzm])); - - // J+x - // NOLINTNEXTLINE - BoutReal Jpx = - (Gxp[jz] * (Fxp[jzp] - Fxp[jzm]) - Gxm[jz] * (Fxm[jzp] - Fxm[jzm]) - - Gx[jzp] * (Fxp[jzp] - Fxm[jzp]) + Gx[jzm] * (Fxp[jzm] - Fxm[jzm])); - - // Jx+ - // NOLINTNEXTLINE - BoutReal Jxp = - (Gxp[jzp] * (Fx[jzp] - Fxp[jz]) - Gxm[jzm] * (Fxm[jz] - Fx[jzm]) - - Gxm[jzp] * (Fx[jzp] - Fxm[jz]) + Gxp[jzm] * (Fxp[jz] - Fx[jzm])); - - result(jx, jy, jz) = (Jpp + Jpx + Jxp) * spacingFactor; - } - } - } - - break; - } case BRACKET_SIMPLE: { // Use a subset of terms for comparison to BOUT-06 result = VDDX(DDZ(f, outloc), g, outloc) + VDDZ(-DDX(f, outloc), g, outloc); diff --git a/src/mesh/fv_ops.cxx b/src/mesh/fv_ops.cxx index f6e427232c..930a2d18ee 100644 --- a/src/mesh/fv_ops.cxx +++ b/src/mesh/fv_ops.cxx @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -47,7 +46,7 @@ Field3D Div_a_Grad_perp(const Field3D& a, const Field3D& f) { for (int i = xs; i <= xe; i++) { for (int j = mesh->ystart; j <= mesh->yend; j++) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { // Calculate flux from i to i+1 BoutReal fout = 0.5 * (a(i, j, k) + a(i + 1, j, k)) @@ -180,7 +179,6 @@ Field3D Div_a_Grad_perp(const Field3D& a, const Field3D& f) { const Field3D Div_par_K_Grad_par(const Field3D& Kin, const Field3D& fin, bool bndry_flux) { - TRACE("FV::Div_par_K_Grad_par"); ASSERT2(Kin.getLocation() == fin.getLocation()); @@ -281,7 +279,7 @@ const Field3D D4DY4(const Field3D& d_in, const Field3D& f_in) { mesh->yend; for (int j = ystart; j <= yend; j++) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { BoutReal dy3 = SQ(coord->dy(i, j, k)) * coord->dy(i, j, k); // 3rd derivative at upper boundary @@ -328,7 +326,7 @@ const Field3D D4DY4_Index(const Field3D& f_in, bool bndry_flux) { if (j != mesh->yend || !has_upper_boundary) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { // Right boundary common factors const BoutReal common_factor = 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) @@ -352,7 +350,7 @@ const Field3D D4DY4_Index(const Field3D& f_in, bool bndry_flux) { // At a domain boundary // Use a one-sided difference formula - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { // Right boundary common factors const BoutReal common_factor = 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) @@ -382,7 +380,7 @@ const Field3D D4DY4_Index(const Field3D& f_in, bool bndry_flux) { // Calculate the fluxes if (j != mesh->ystart || !has_lower_boundary) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { const BoutReal common_factor = 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) * (coord->J(i, j, k) + coord->J(i, j - 1, k)); @@ -401,7 +399,7 @@ const Field3D D4DY4_Index(const Field3D& f_in, bool bndry_flux) { } } else { // On a domain (Y) boundary - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { const BoutReal common_factor = 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) * (coord->J(i, j, k) + coord->J(i, j - 1, k)); @@ -441,8 +439,9 @@ void communicateFluxes(Field3D& f) { comm_handle xin, xout; // Cache results to silence spurious compiler warning about xin, // xout possibly being uninitialised when used - bool not_first = !mesh->firstX(); - bool not_last = !mesh->lastX(); + const bool not_first = mesh->periodicX || !mesh->firstX(); + const bool not_last = mesh->periodicX || !mesh->lastX(); + if (not_first) { xin = mesh->irecvXIn(f(0, 0), size, 0); } @@ -461,7 +460,7 @@ void communicateFluxes(Field3D& f) { mesh->wait(xin); // Add to cells for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(2, y, z) += f(0, y, z); } } @@ -470,7 +469,7 @@ void communicateFluxes(Field3D& f) { mesh->wait(xout); // Add to cells for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(mesh->LocalNx - 3, y, z) += f(mesh->LocalNx - 1, y, z); } } diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 31319b87d1..7ab45c327a 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -2,19 +2,10 @@ * Implementation of the Mesh class, handling input files compatible with * BOUT / BOUT-06. * - * Changelog - * --------- - * - * 2015-01 Ben Dudson - * * - * - * 2010-05 Ben Dudson - * * Initial version, adapted from grid.cpp and topology.cpp - * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010-2025 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -35,24 +26,46 @@ #include "boutmesh.hxx" +#include #include +#include +#include #include #include #include #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include +#include +#include +#include #include #include +#include +#include + #include +#include +#include #include +#include +#include +#include #include +#include +#include +#include /// MPI type of BoutReal for communications #define PVEC_REAL_MPI_TYPE MPI_DOUBLE @@ -66,6 +79,8 @@ If you want the old setting, you have to specify mesh:symmetricGlobalY=false in << optionfile << "\n"; } OPTION(options, symmetricGlobalY, true); + OPTION(options, symmetricGlobalZ, false); // The default should be updated to true but + // this breaks backwards compatibility comm_x = MPI_COMM_NULL; comm_inner = MPI_COMM_NULL; @@ -93,6 +108,9 @@ BoutMesh::~BoutMesh() { if (comm_outer != MPI_COMM_NULL) { MPI_Comm_free(&comm_outer); } + if (comm_xz != MPI_COMM_NULL) { + MPI_Comm_free(&comm_xz); + } } BoutMesh::YDecompositionIndices @@ -172,13 +190,13 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, // Check size of Y mesh if we've got multiple processors in Y if (num_local_y_points < num_y_guards and num_y_processors != 1) { return {false, - fmt::format(_("\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n"), ny, - num_y_processors, num_local_y_points, num_y_guards)}; + fmt::format(_f("\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n"), + ny, num_y_processors, num_local_y_points, num_y_guards)}; } // Check branch cuts if ((jyseps1_1 + 1) % num_local_y_points != 0) { - return {false, fmt::format(_("\t -> Leg region jyseps1_1+1 ({:d}) must be a " - "multiple of MYSUB ({:d})\n"), + return {false, fmt::format(_f("\t -> Leg region jyseps1_1+1 ({:d}) must be a " + "multiple of MYSUB ({:d})\n"), jyseps1_1 + 1, num_local_y_points)}; } @@ -188,50 +206,51 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((jyseps2_1 - jyseps1_1) % num_local_y_points != 0) { return { false, - fmt::format(_("\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), + fmt::format(_f("\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps2_1, jyseps1_1, jyseps2_1 - jyseps1_1, num_local_y_points)}; } if ((jyseps2_2 - jyseps1_2) % num_local_y_points != 0) { return { false, - fmt::format(_("\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), + fmt::format(_f("\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps2_2, jyseps1_2, jyseps2_2 - jyseps1_2, num_local_y_points)}; } // Check upper legs if ((ny_inner - jyseps2_1 - 1) % num_local_y_points != 0) { - return { - false, - fmt::format(_("\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), - ny_inner, jyseps2_1, ny_inner - jyseps2_1 - 1, num_local_y_points)}; + return {false, + fmt::format( + _f("\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), + ny_inner, jyseps2_1, ny_inner - jyseps2_1 - 1, num_local_y_points)}; } if ((jyseps1_2 - ny_inner + 1) % num_local_y_points != 0) { - return { - false, - fmt::format(_("\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), - jyseps1_2, ny_inner, jyseps1_2 - ny_inner + 1, num_local_y_points)}; + return {false, + fmt::format( + _f("\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), + jyseps1_2, ny_inner, jyseps1_2 - ny_inner + 1, num_local_y_points)}; } } else { // Single Null if ((jyseps2_2 - jyseps1_1) % num_local_y_points != 0) { return { false, - fmt::format(_("\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), + fmt::format(_f("\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps2_2, jyseps1_1, jyseps2_2 - jyseps1_1, num_local_y_points)}; } } if ((ny - jyseps2_2 - 1) % num_local_y_points != 0) { - return {false, fmt::format( - _("\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a " - "multiple of MYSUB ({:d})\n"), - ny, jyseps2_2, ny - jyseps2_2 - 1, num_local_y_points)}; + return { + false, + fmt::format(_f("\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a " + "multiple of MYSUB ({:d})\n"), + ny, jyseps2_2, ny - jyseps2_2 - 1, num_local_y_points)}; } return {true, ""}; @@ -251,10 +270,14 @@ void BoutMesh::chooseProcessorSplit(Options& options) { .withDefault(1); if ((NPES % NXPE) != 0) { throw BoutException( - _("Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n"), + _f("Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n"), NPES, NXPE); } - + if ((nx - 2 * MXG) % NXPE != 0) { + throw BoutException( + _f("Number of x points ({:d}) not divisible by NPs in x direction ({:d})\n"), + nx - 2 * MXG, NXPE); + } NYPE = NPES / NXPE; } else { // NXPE not set, but NYPE is @@ -264,10 +287,14 @@ void BoutMesh::chooseProcessorSplit(Options& options) { .withDefault(1); if ((NPES % NYPE) != 0) { throw BoutException( - _("Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n"), + _f("Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n"), NPES, NYPE); } - + if (ny % NYPE != 0) { + throw BoutException( + _f("Number of y points ({:d}) not divisible by NPs in y direction ({:d})\n"), + nx, NXPE); + } NXPE = NPES / NYPE; } @@ -287,14 +314,14 @@ void BoutMesh::findProcessorSplit() { // Results in square domains const BoutReal ideal = sqrt(MX * NPES / static_cast(ny)); - output_info.write(_("Finding value for NXPE (ideal = {:f})\n"), ideal); + output_info.write(_f("Finding value for NXPE (ideal = {:f})\n"), ideal); for (int i = 1; i <= NPES; i++) { // Loop over all possibilities if ((NPES % i == 0) && // Processors divide equally (MX % i == 0) && // Mesh in X divides equally (ny % (NPES / i) == 0)) { // Mesh in Y divides equally - output_info.write(_("\tCandidate value: {:d}\n"), i); + output_info.write(_f("\tCandidate value: {:d}\n"), i); const int nyp = NPES / i; @@ -321,15 +348,15 @@ void BoutMesh::findProcessorSplit() { NYPE = NPES / NXPE; - output_progress.write(_("\tDomain split (NXPE={:d}, NYPE={:d}) into domains " - "(localNx={:d}, localNy={:d})\n"), + output_progress.write(_f("\tDomain split (NXPE={:d}, NYPE={:d}) into domains " + "(localNx={:d}, localNy={:d})\n"), NXPE, NYPE, MX / NXPE, ny / NYPE); } void BoutMesh::setDerivedGridSizes() { // Check that nx is large enough if (nx <= 2 * MXG) { - throw BoutException(_("Error: nx must be greater than 2 times MXG (2 * {:d})"), MXG); + throw BoutException(_f("Error: nx must be greater than 2 times MXG (2 * {:d})"), MXG); } GlobalNx = nx; @@ -354,8 +381,8 @@ void BoutMesh::setDerivedGridSizes() { MX = nx - 2 * MXG; MXSUB = MX / NXPE; if ((MX % NXPE) != 0) { - throw BoutException(_("Cannot split {:d} X points equally between {:d} processors\n"), - MX, NXPE); + throw BoutException( + _f("Cannot split {:d} X points equally between {:d} processors\n"), MX, NXPE); } // NOTE: No grid data reserved for Y boundary cells - copy from neighbours @@ -363,7 +390,7 @@ void BoutMesh::setDerivedGridSizes() { MYSUB = MY / NYPE; if ((MY % NYPE) != 0) { throw BoutException( - _("\tERROR: Cannot split {:d} Y points equally between {:d} processors\n"), MY, + _f("\tERROR: Cannot split {:d} Y points equally between {:d} processors\n"), MY, NYPE); } @@ -371,7 +398,7 @@ void BoutMesh::setDerivedGridSizes() { MZSUB = MZ / NZPE; if ((MZ % NZPE) != 0) { throw BoutException( - _("\tERROR: Cannot split {:d} Z points equally between {:d} processors\n"), MZ, + _f("\tERROR: Cannot split {:d} Z points equally between {:d} processors\n"), MZ, NZPE); } @@ -440,7 +467,6 @@ void BoutMesh::setDerivedGridSizes() { } int BoutMesh::load() { - TRACE("BoutMesh::load()"); output_progress << _("Loading mesh") << endl; @@ -473,8 +499,8 @@ int BoutMesh::load() { if (!is_pow2(nz)) { // Should be a power of 2 for efficient FFTs output_warn.write( - _("WARNING: Number of toroidal points should be 2^n for efficient " - "FFT performance -- consider changing MZ ({:d}) if using FFTs\n"), + _f("WARNING: Number of toroidal points should be 2^n for efficient " + "FFT performance -- consider changing MZ ({:d}) if using FFTs\n"), nz); } } else { @@ -493,8 +519,19 @@ int BoutMesh::load() { } ASSERT0(MXG >= 0); - if (Mesh::get(MYG, "MYG") != 0) { - MYG = options["MYG"].doc("Number of guard cells on each side in Y").withDefault(2); + const bool meshHasMyg = Mesh::get(MYG, "MYG") == 0; + if (!meshHasMyg) { + MYG = 2; + } + int meshMyg = MYG; + + if (options.isSet("MYG") or (!meshHasMyg)) { + MYG = options["MYG"].doc("Number of guard cells on each side in Y").withDefault(MYG); + } + if (meshHasMyg && MYG != meshMyg) { + output_warn.write(_f("Options changed the number of y-guard cells. Grid has {} but " + "option specified {}! Continuing with {}"), + meshMyg, MYG, MYG); } ASSERT0(MYG >= 0); @@ -526,9 +563,12 @@ int BoutMesh::load() { findProcessorSplit(); } - // Get X and Y processor indices + // Get X, Y, Z processor indices PE_YIND = MYPE / NXPE; PE_XIND = MYPE % NXPE; + PE_ZIND = 0; + + ASSERT2(MYPE == getProcIndex(PE_XIND, PE_YIND, PE_ZIND)); // Set the other grid sizes from nx, ny, nz setDerivedGridSizes(); @@ -619,15 +659,67 @@ int BoutMesh::load() { // Initialize default coordinates getCoordinates(); + // Set cached values + if (isFci()) { + has_boundary_lower_y = false; + has_boundary_upper_y = false; + } else { + { + int mybndry = static_cast(!(iterateBndryLowerY().isDone())); + int allbndry = 0; + mpi->MPI_Allreduce(&mybndry, &allbndry, 1, MPI_INT, MPI_BOR, getXcomm(yend)); + has_boundary_lower_y = static_cast(allbndry); + } + { + int mybndry = static_cast(!(iterateBndryUpperY().isDone())); + int allbndry = 0; + mpi->MPI_Allreduce(&mybndry, &allbndry, 1, MPI_INT, MPI_BOR, getXcomm(ystart)); + has_boundary_upper_y = static_cast(allbndry); + } + } + output_info.write(_("\tdone\n")); return 0; } +namespace { +auto make_XZ_communicator(const BoutMesh& mesh, MPI_Group group_world) -> MPI_Comm { + std::vector ranks; + + const int yp = mesh.getYProcIndex(); + + // All processors with the same Y index + for (int xp = 0; xp < mesh.getNXPE(); ++xp) { + for (int zp = 0; zp < mesh.getNZPE(); ++zp) { + ranks.push_back(mesh.getProcIndex(xp, yp, zp)); + } + } + MPI_Group group{}; + if (MPI_Group_incl(group_world, static_cast(ranks.size()), ranks.data(), &group) + != MPI_SUCCESS) { + throw BoutException("Could not create X-Z communication group for ranks {}", + fmt::join(ranks, ", ")); + } + + MPI_Comm comm_xz{}; + if (MPI_Comm_create(BoutComm::get(), group, &comm_xz) != MPI_SUCCESS) { + throw BoutException("Could not create X-Z communicator for yp={} (xind={}, yind={}, " + "zind={}) ranks={}", + yp, mesh.getXProcIndex(), mesh.getYProcIndex(), + mesh.getZProcIndex(), fmt::join(ranks, ", ")); + } + + return comm_xz; +} +} // namespace + void BoutMesh::createCommunicators() { MPI_Group group_world{}; MPI_Comm_group(BoutComm::get(), &group_world); // Get the entire group + comm_xz = make_XZ_communicator(*this, group_world); + ////////////////////////////////////////////////////// /// Communicator in X @@ -984,19 +1076,24 @@ void BoutMesh::createXBoundaries() { if (((yg > jyseps1_1) and (yg <= jyseps2_1)) or ((yg > jyseps1_2) and (yg <= jyseps2_2))) { // Core - boundary.push_back(new BoundaryRegionXIn("core", ystart, yend, this)); + boundary.push_back( + bout::boundary::NewBoundaryRegionXIn("core", ystart, yend, this)); } else { // PF region - boundary.push_back(new BoundaryRegionXIn("pf", ystart, yend, this)); + boundary.push_back(bout::boundary::NewBoundaryRegionXIn("pf", ystart, yend, this)); } } if (PE_XIND == (NXPE - 1)) { // Outer SOL - boundary.push_back(new BoundaryRegionXOut("sol", ystart, yend, this)); + boundary.push_back(bout::boundary::NewBoundaryRegionXOut("sol", ystart, yend, this)); } } +int BoutMesh::getProcIndex(int X, int Y, [[maybe_unused]] int Z) const { + return (((Z * NYPE) + Y) * NXPE) + X; +} + void BoutMesh::createYBoundaries() { if (MYG <= 0) { return; @@ -1016,21 +1113,21 @@ void BoutMesh::createYBoundaries() { (include_corner_cells and ODATA_DEST == -1) ? LocalNx - 1 : xend; if ((UDATA_INDEST < 0) && (UDATA_XSPLIT > yboundary_xstart)) { - boundary.push_back( - new BoundaryRegionYUp("upper_target", yboundary_xstart, UDATA_XSPLIT - 1, this)); + boundary.push_back(bout::boundary::NewBoundaryRegionYUp( + "upper_target", yboundary_xstart, UDATA_XSPLIT - 1, this)); } if ((UDATA_OUTDEST < 0) && (UDATA_XSPLIT <= yboundary_xend)) { - boundary.push_back( - new BoundaryRegionYUp("upper_target", UDATA_XSPLIT, yboundary_xend, this)); + boundary.push_back(bout::boundary::NewBoundaryRegionYUp("upper_target", UDATA_XSPLIT, + yboundary_xend, this)); } if ((DDATA_INDEST < 0) && (DDATA_XSPLIT > yboundary_xstart)) { - boundary.push_back(new BoundaryRegionYDown("lower_target", yboundary_xstart, - DDATA_XSPLIT - 1, this)); + boundary.push_back(bout::boundary::NewBoundaryRegionYDown( + "lower_target", yboundary_xstart, DDATA_XSPLIT - 1, this)); } if ((DDATA_OUTDEST < 0) && (DDATA_XSPLIT <= yboundary_xend)) { - boundary.push_back( - new BoundaryRegionYDown("lower_target", DDATA_XSPLIT, yboundary_xend, this)); + boundary.push_back(bout::boundary::NewBoundaryRegionYDown( + "lower_target", DDATA_XSPLIT, yboundary_xend, this)); } } @@ -1358,7 +1455,6 @@ comm_handle BoutMesh::sendY(FieldGroup& g, comm_handle handle) { } int BoutMesh::wait(comm_handle handle) { - TRACE("BoutMesh::wait(comm_handle)"); if (handle == nullptr) { return 1; @@ -1516,13 +1612,17 @@ int BoutMesh::wait(comm_handle handle) { * Non-Local Communications ***************************************************************/ -int BoutMesh::getNXPE() { return NXPE; } +int BoutMesh::getNXPE() const { return NXPE; } + +int BoutMesh::getNYPE() const { return NYPE; } + +int BoutMesh::getNZPE() const { return NZPE; } -int BoutMesh::getNYPE() { return NYPE; } +int BoutMesh::getXProcIndex() const { return PE_XIND; } -int BoutMesh::getXProcIndex() { return PE_XIND; } +int BoutMesh::getYProcIndex() const { return PE_YIND; } -int BoutMesh::getYProcIndex() { return PE_YIND; } +int BoutMesh::getZProcIndex() const { return PE_ZIND; } /**************************************************************** * X COMMUNICATIONS @@ -1535,43 +1635,65 @@ bool BoutMesh::firstX() const { return PE_XIND == 0; } bool BoutMesh::lastX() const { return PE_XIND == NXPE - 1; } int BoutMesh::sendXOut(BoutReal* buffer, int size, int tag) { + Timer timer("comms"); + + int proc{-1}; if (PE_XIND == NXPE - 1) { - return 1; + if (periodicX) { + // Wrap around to first processor in X + proc = PROC_NUM(0, PE_YIND); + } else { + return 1; + } + } else { + proc = PROC_NUM(PE_XIND + 1, PE_YIND); } - Timer timer("comms"); - - mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, PROC_NUM(PE_XIND + 1, PE_YIND), tag, - BoutComm::get()); + mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get()); return 0; } int BoutMesh::sendXIn(BoutReal* buffer, int size, int tag) { + Timer timer("comms"); + + int proc{-1}; if (PE_XIND == 0) { - return 1; + if (periodicX) { + // Wrap around to last processor in X + proc = PROC_NUM(NXPE - 1, PE_YIND); + } else { + return 1; + } + } else { + proc = PROC_NUM(PE_XIND - 1, PE_YIND); } - Timer timer("comms"); - - mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, PROC_NUM(PE_XIND - 1, PE_YIND), tag, - BoutComm::get()); + mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get()); return 0; } comm_handle BoutMesh::irecvXOut(BoutReal* buffer, int size, int tag) { + Timer timer("comms"); + + int proc{-1}; if (PE_XIND == NXPE - 1) { - return nullptr; + if (periodicX) { + // Wrap around to first processor in X + proc = PROC_NUM(0, PE_YIND); + } else { + return nullptr; + } + } else { + proc = PROC_NUM(PE_XIND + 1, PE_YIND); } - Timer timer("comms"); - // Get a communications handle. Not fussy about size of arrays CommHandle* ch = get_handle(0, 0); - mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, PROC_NUM(PE_XIND + 1, PE_YIND), tag, - BoutComm::get(), ch->request); + mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get(), + ch->request); ch->in_progress = true; @@ -1579,17 +1701,25 @@ comm_handle BoutMesh::irecvXOut(BoutReal* buffer, int size, int tag) { } comm_handle BoutMesh::irecvXIn(BoutReal* buffer, int size, int tag) { + Timer timer("comms"); + + int proc{-1}; if (PE_XIND == 0) { - return nullptr; + if (periodicX) { + // Wrap around to last processor in X + proc = PROC_NUM(NXPE - 1, PE_YIND); + } else { + return nullptr; + } + } else { + proc = PROC_NUM(PE_XIND - 1, PE_YIND); } - Timer timer("comms"); - // Get a communications handle. Not fussy about size of arrays CommHandle* ch = get_handle(0, 0); - mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, PROC_NUM(PE_XIND - 1, PE_YIND), tag, - BoutComm::get(), ch->request); + mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get(), + ch->request); ch->in_progress = true; @@ -1654,35 +1784,32 @@ int BoutMesh::PROC_NUM(int xind, int yind) const { return -1; } - return yind * NXPE + xind; + return (yind * NXPE) + xind; } -/// Returns the global X index given a local index -int BoutMesh::XGLOBAL(BoutReal xloc, BoutReal& xglo) const { - xglo = xloc + PE_XIND * MXSUB; - return static_cast(xglo); +BoutReal BoutMesh::getGlobalXIndex(BoutReal xloc) const { + return xloc + (PE_XIND * MXSUB); } -int BoutMesh::getGlobalXIndex(int xlocal) const { return xlocal + PE_XIND * MXSUB; } +int BoutMesh::getGlobalXIndex(int xlocal) const { return xlocal + (PE_XIND * MXSUB); } int BoutMesh::getGlobalXIndexNoBoundaries(int xlocal) const { - return xlocal + PE_XIND * MXSUB - MXG; + return xlocal + (PE_XIND * MXSUB) - MXG; } -int BoutMesh::getLocalXIndex(int xglobal) const { return xglobal - PE_XIND * MXSUB; } +int BoutMesh::getLocalXIndex(int xglobal) const { return xglobal - (PE_XIND * MXSUB); } int BoutMesh::getLocalXIndexNoBoundaries(int xglobal) const { - return xglobal - PE_XIND * MXSUB + MXG; + return xglobal - (PE_XIND * MXSUB) + MXG; } -int BoutMesh::YGLOBAL(BoutReal yloc, BoutReal& yglo) const { - yglo = yloc + PE_YIND * MYSUB - MYG; - return static_cast(yglo); +BoutReal BoutMesh::getGlobalYIndex(BoutReal yloc) const { + return yloc + (PE_YIND * MYSUB) - MYG; } int BoutMesh::getGlobalYIndex(int ylocal) const { - int yglobal = ylocal + PE_YIND * MYSUB; - if (jyseps1_2 > jyseps2_1 and PE_YIND * MYSUB + 2 * MYG + 1 > ny_inner) { + int yglobal = ylocal + (PE_YIND * MYSUB); + if (jyseps1_2 > jyseps2_1 and (PE_YIND * MYSUB) + (2 * MYG) + 1 > ny_inner) { // Double null, and we are past the upper target yglobal += 2 * MYG; } @@ -1690,12 +1817,12 @@ int BoutMesh::getGlobalYIndex(int ylocal) const { } int BoutMesh::getGlobalYIndexNoBoundaries(int ylocal) const { - return ylocal + PE_YIND * MYSUB - MYG; + return ylocal + (PE_YIND * MYSUB) - MYG; } int BoutMesh::getLocalYIndex(int yglobal) const { - int ylocal = yglobal - PE_YIND * MYSUB; - if (jyseps1_2 > jyseps2_1 and PE_YIND * MYSUB + 2 * MYG + 1 > ny_inner) { + int ylocal = yglobal - (PE_YIND * MYSUB); + if (jyseps1_2 > jyseps2_1 and (PE_YIND * MYSUB) + (2 * MYG) + 1 > ny_inner) { // Double null, and we are past the upper target ylocal -= 2 * MYG; } @@ -1703,19 +1830,25 @@ int BoutMesh::getLocalYIndex(int yglobal) const { } int BoutMesh::getLocalYIndexNoBoundaries(int yglobal) const { - return yglobal - PE_YIND * MYSUB + MYG; + return yglobal - (PE_YIND * MYSUB) + MYG; } -int BoutMesh::YGLOBAL(int yloc, int yproc) const { return yloc + yproc * MYSUB - MYG; } +int BoutMesh::YGLOBAL(int yloc, int yproc) const { return yloc + (yproc * MYSUB) - MYG; } -int BoutMesh::YLOCAL(int yglo, int yproc) const { return yglo - yproc * MYSUB + MYG; } +int BoutMesh::YLOCAL(int yglo, int yproc) const { return yglo - (yproc * MYSUB) + MYG; } -int BoutMesh::getGlobalZIndex(int zlocal) const { return zlocal; } +int BoutMesh::getGlobalZIndex(int zlocal) const { return zlocal + (PE_ZIND * MZSUB); } -int BoutMesh::getGlobalZIndexNoBoundaries(int zlocal) const { return zlocal; } +int BoutMesh::getGlobalZIndexNoBoundaries(int zlocal) const { + return zlocal + (PE_ZIND * MZSUB) - MZG; +} int BoutMesh::getLocalZIndex(int zglobal) const { return zglobal; } +BoutReal BoutMesh::getGlobalZIndex(BoutReal zloc) const { + return zloc + (PE_ZIND * MZSUB); +} + int BoutMesh::getLocalZIndexNoBoundaries(int zglobal) const { return zglobal; } int BoutMesh::YPROC(int yind) const { @@ -1783,16 +1916,15 @@ BoutMesh::BoutMesh(int input_nx, int input_ny, int input_nz, int mxg, int myg, i BoutMesh::BoutMesh(int input_nx, int input_ny, int input_nz, int mxg, int myg, int nxpe, int nype, int pe_xind, int pe_yind, bool symmetric_X, bool symmetric_Y, - bool periodicX_, int ixseps1_, int ixseps2_, int jyseps1_1_, + bool periodic_X_, int ixseps1_, int ixseps2_, int jyseps1_1_, int jyseps2_1_, int jyseps1_2_, int jyseps2_2_, int ny_inner_, bool create_regions) : nx(input_nx), ny(input_ny), nz(input_nz), NPES(nxpe * nype), - MYPE(nxpe * pe_yind + pe_xind), PE_YIND(pe_yind), NYPE(nype), NZPE(1), - ixseps1(ixseps1_), ixseps2(ixseps2_), symmetricGlobalX(symmetric_X), + MYPE((nxpe * pe_yind) + pe_xind), PE_XIND(pe_xind), NXPE(nxpe), PE_YIND(pe_yind), + NYPE(nype), ixseps1(ixseps1_), ixseps2(ixseps2_), symmetricGlobalX(symmetric_X), symmetricGlobalY(symmetric_Y), MXG(mxg), MYG(myg), MZG(0) { - NXPE = nxpe; - PE_XIND = pe_xind; - periodicX = periodicX_; + + periodicX = periodic_X_; setYDecompositionIndices(jyseps1_1_, jyseps2_1_, jyseps1_2_, jyseps2_2_, ny_inner_); setDerivedGridSizes(); topology(); @@ -2118,6 +2250,37 @@ void BoutMesh::topology() { add_target(ny_inner - 1, 0, nx); } + // Additional limiters + // Each limiter needs 3 indices: A Y index, start and end X indices + int limiter_count = 0; + Mesh::get(limiter_count, "limiter_count", 0); + if (limiter_count > 0) { + std::vector limiter_yinds; + if (!source->get(this, limiter_yinds, "limiter_yinds", limiter_count)) { + throw BoutException("Couldn't read limiter_yinds vector of length {} from mesh", + limiter_count); + } + std::vector limiter_xstarts; + if (!source->get(this, limiter_xstarts, "limiter_xstarts", limiter_count)) { + throw BoutException("Couldn't read limiter_xstarts vector of length {} from mesh", + limiter_count); + } + std::vector limiter_xends; + if (!source->get(this, limiter_xends, "limiter_xends", limiter_count)) { + throw BoutException("Couldn't read limiter_xend vector of length {} from mesh", + limiter_count); + } + + for (int i = 0; i < limiter_count; ++i) { + const int yind = limiter_yinds[i]; + const int xstart = limiter_xstarts[i]; + const int xend = limiter_xends[i]; + output_info.write("Adding a limiter between y={} and {}. X indices {} to {}\n", + yind, yind + 1, xstart, xend); + add_target(yind, xstart, xend); + } + } + if ((ixseps_inner > 0) && (((PE_YIND * MYSUB > jyseps1_1) && (PE_YIND * MYSUB <= jyseps2_1)) || ((PE_YIND * MYSUB > jyseps1_2) && (PE_YIND * MYSUB <= jyseps2_2)))) { @@ -2288,72 +2451,112 @@ void BoutMesh::overlapHandleMemory(BoutMesh* yup, BoutMesh* ydown, BoutMesh* xin * Communication utilities ****************************************************************/ -int BoutMesh::pack_data(const std::vector& var_list, int xge, int xlt, - int yge, int ylt, BoutReal* buffer) { +int BoutMesh::pack_data(const std::vector& var_list, int xge, int xlt, int yge, + int ylt, BoutReal* buffer) const { + using enum Field::FieldType; int len = 0; + const int zge = 0; + const int zlt = LocalNz; - /// Loop over variables for (const auto& var : var_list) { - if (var->is3D()) { - // 3D variable - auto* var3d_ref_ptr = dynamic_cast(var); + switch (var->field_type()) { + case field3d: { + const auto* var3d_ref_ptr = dynamic_cast(var); ASSERT0(var3d_ref_ptr != nullptr); - auto& var3d_ref = *var3d_ref_ptr; + const auto& var3d_ref = *var3d_ref_ptr; ASSERT2(var3d_ref.isAllocated()); - for (int jx = xge; jx != xlt; jx++) { + for (int jx = xge; jx < xlt; jx++) { for (int jy = yge; jy < ylt; jy++) { - for (int jz = 0; jz < LocalNz; jz++, len++) { + for (int jz = zge; jz < zlt; jz++, len++) { buffer[len] = var3d_ref(jx, jy, jz); } } } - } else { - // 2D variable - auto* var2d_ref_ptr = dynamic_cast(var); + break; + } + case field2d: { + const auto* var2d_ref_ptr = dynamic_cast(var); ASSERT0(var2d_ref_ptr != nullptr); - auto& var2d_ref = *var2d_ref_ptr; + const auto& var2d_ref = *var2d_ref_ptr; ASSERT2(var2d_ref.isAllocated()); - for (int jx = xge; jx != xlt; jx++) { + for (int jx = xge; jx < xlt; jx++) { for (int jy = yge; jy < ylt; jy++, len++) { buffer[len] = var2d_ref(jx, jy); } } + break; + } + case fieldperp: { + const auto* varperp_ref_ptr = dynamic_cast(var); + ASSERT0(varperp_ref_ptr != nullptr); + const auto& varperp_ref = *varperp_ref_ptr; + ASSERT2(varperp_ref.isAllocated()); + for (int jx = xge; jx < xlt; jx++) { + for (int jz = zge; jz < zlt; jz++, len++) { + buffer[len] = varperp_ref(jx, jz); + } + } + break; + } } } - return (len); + return len; } -int BoutMesh::unpack_data(const std::vector& var_list, int xge, int xlt, - int yge, int ylt, BoutReal* buffer) { +int BoutMesh::unpack_data(const std::vector& var_list, int xge, int xlt, int yge, + int ylt, const BoutReal* buffer) const { + using enum Field::FieldType; int len = 0; + const int zge = 0; + const int zlt = LocalNz; - /// Loop over variables for (const auto& var : var_list) { - if (var->is3D()) { - // 3D variable - auto& var3d_ref = *dynamic_cast(var); - for (int jx = xge; jx != xlt; jx++) { + switch (var->field_type()) { + case field3d: { + auto* var3d_ref_ptr = dynamic_cast(var); + ASSERT0(var3d_ref_ptr != nullptr); + auto& var3d_ref = *var3d_ref_ptr; + ASSERT2(var3d_ref.isAllocated()); + for (int jx = xge; jx < xlt; jx++) { for (int jy = yge; jy < ylt; jy++) { - for (int jz = 0; jz < LocalNz; jz++, len++) { + for (int jz = zge; jz < zlt; jz++, len++) { var3d_ref(jx, jy, jz) = buffer[len]; } } } - } else { - // 2D variable - auto& var2d_ref = *dynamic_cast(var); - for (int jx = xge; jx != xlt; jx++) { + break; + } + case field2d: { + auto* var2d_ref_ptr = dynamic_cast(var); + ASSERT0(var2d_ref_ptr != nullptr); + auto& var2d_ref = *var2d_ref_ptr; + ASSERT2(var2d_ref.isAllocated()); + for (int jx = xge; jx < xlt; jx++) { for (int jy = yge; jy < ylt; jy++, len++) { var2d_ref(jx, jy) = buffer[len]; } } + break; + } + case fieldperp: { + auto* varperp_ref_ptr = dynamic_cast(var); + ASSERT0(varperp_ref_ptr != nullptr); + auto& varperp_ref = *varperp_ref_ptr; + ASSERT2(varperp_ref.isAllocated()); + for (int jx = xge; jx < xlt; jx++) { + for (int jz = zge; jz < zlt; jz++, len++) { + varperp_ref(jx, jz) = buffer[len]; + } + } + break; + } } } - return (len); + return len; } /**************************************************************** @@ -2819,6 +3022,11 @@ void BoutMesh::addBoundaryRegions() { } RangeIterator BoutMesh::iterateBndryLowerInnerY() const { +#if CHECK > 0 + if (this->isFci()) { + throw BoutException("FCI should never use this iterator"); + } +#endif int xs = 0; int xe = LocalNx - 1; @@ -2854,6 +3062,11 @@ RangeIterator BoutMesh::iterateBndryLowerInnerY() const { } RangeIterator BoutMesh::iterateBndryLowerOuterY() const { +#if CHECK > 0 + if (this->isFci()) { + throw BoutException("FCI should never use this iterator"); + } +#endif int xs = 0; int xe = LocalNx - 1; @@ -2888,6 +3101,12 @@ RangeIterator BoutMesh::iterateBndryLowerOuterY() const { } RangeIterator BoutMesh::iterateBndryLowerY() const { +#if CHECK > 0 + if (this->isFci()) { + throw BoutException("FCI should never use this iterator"); + } +#endif + int xs = 0; int xe = LocalNx - 1; if ((DDATA_INDEST >= 0) && (DDATA_XSPLIT > xstart)) { @@ -2917,6 +3136,12 @@ RangeIterator BoutMesh::iterateBndryLowerY() const { } RangeIterator BoutMesh::iterateBndryUpperInnerY() const { +#if CHECK > 0 + if (this->isFci()) { + throw BoutException("FCI should never use this iterator"); + } +#endif + int xs = 0; int xe = LocalNx - 1; @@ -2951,6 +3176,12 @@ RangeIterator BoutMesh::iterateBndryUpperInnerY() const { } RangeIterator BoutMesh::iterateBndryUpperOuterY() const { +#if CHECK > 0 + if (this->isFci()) { + throw BoutException("FCI should never use this iterator"); + } +#endif + int xs = 0; int xe = LocalNx - 1; @@ -2985,6 +3216,12 @@ RangeIterator BoutMesh::iterateBndryUpperOuterY() const { } RangeIterator BoutMesh::iterateBndryUpperY() const { +#if CHECK > 0 + if (this->isFci()) { + throw BoutException("FCI should never use this iterator"); + } +#endif + int xs = 0; int xe = LocalNx - 1; if ((UDATA_INDEST >= 0) && (UDATA_XSPLIT > xstart)) { @@ -3013,14 +3250,14 @@ RangeIterator BoutMesh::iterateBndryUpperY() const { return RangeIterator(xs, xe); } -std::vector BoutMesh::getBoundaries() { return boundary; } +std::vector BoutMesh::getBoundaries() { return boundary; } -std::vector> -BoutMesh::getBoundariesPar(BoundaryParType type) { +using bout::boundary::BoundaryRegionFCI; +std::vector> +BoutMesh::getBoundariesPar(BoundaryParType type) const { return par_boundary[static_cast(type)]; } - -void BoutMesh::addBoundaryPar(std::shared_ptr bndry, +void BoutMesh::addBoundaryPar(std::shared_ptr bndry, BoundaryParType type) { output_info << "Adding new parallel boundary: " << bndry->label << endl; switch (type) { @@ -3047,47 +3284,6 @@ void BoutMesh::addBoundaryPar(std::shared_ptr bndry, par_boundary[static_cast(BoundaryParType::all)].push_back(bndry); } -Field3D BoutMesh::smoothSeparatrix(const Field3D& f) { - Field3D result{emptyFrom(f)}; - if ((ixseps_inner > 0) && (ixseps_inner < nx - 1)) { - if (XPROC(ixseps_inner) == PE_XIND) { - int x = getLocalXIndex(ixseps_inner); - for (int y = 0; y < LocalNy; y++) { - for (int z = 0; z < LocalNz; z++) { - result(x, y, z) = 0.5 * (f(x, y, z) + f(x - 1, y, z)); - } - } - } - if (XPROC(ixseps_inner - 1) == PE_XIND) { - int x = getLocalXIndex(ixseps_inner - 1); - for (int y = 0; y < LocalNy; y++) { - for (int z = 0; z < LocalNz; z++) { - result(x, y, z) = 0.5 * (f(x, y, z) + f(x + 1, y, z)); - } - } - } - } - if ((ixseps_outer > 0) && (ixseps_outer < nx - 1) && (ixseps_outer != ixseps_inner)) { - if (XPROC(ixseps_outer) == PE_XIND) { - int x = getLocalXIndex(ixseps_outer); - for (int y = 0; y < LocalNy; y++) { - for (int z = 0; z < LocalNz; z++) { - result(x, y, z) = 0.5 * (f(x, y, z) + f(x - 1, y, z)); - } - } - } - if (XPROC(ixseps_outer - 1) == PE_XIND) { - int x = getLocalXIndex(ixseps_outer - 1); - for (int y = 0; y < LocalNy; y++) { - for (int z = 0; z < LocalNz; z++) { - result(x, y, z) = 0.5 * (f(x, y, z) + f(x + 1, y, z)); - } - } - } - } - return result; -} - BoutReal BoutMesh::GlobalX(int jx) const { if (symmetricGlobalX) { // With this definition the boundary sits dx/2 away form the first/last inner points @@ -3099,8 +3295,7 @@ BoutReal BoutMesh::GlobalX(int jx) const { BoutReal BoutMesh::GlobalX(BoutReal jx) const { // Get global X index as a BoutReal - BoutReal xglo; - XGLOBAL(jx, xglo); + const BoutReal xglo = getGlobalXIndex(jx); if (symmetricGlobalX) { // With this definition the boundary sits dx/2 away form the first/last inner points @@ -3154,8 +3349,7 @@ BoutReal BoutMesh::GlobalY(int jy) const { BoutReal BoutMesh::GlobalY(BoutReal jy) const { // Get global Y index as a BoutReal - BoutReal yglo; - YGLOBAL(jy, yglo); + BoutReal yglo = getGlobalYIndex(jy); if (symmetricGlobalY) { BoutReal yi = yglo; @@ -3197,6 +3391,28 @@ BoutReal BoutMesh::GlobalY(BoutReal jy) const { return yglo / static_cast(nycore); } +BoutReal BoutMesh::GlobalZ(int jz) const { + if (symmetricGlobalZ) { + // With this definition the boundary sits dz/2 away form the first/last inner points + return (0.5 + getGlobalZIndexNoBoundaries(jz) - (nz - MZ) * 0.5) + / static_cast(MZ); + } + return static_cast(getGlobalZIndexNoBoundaries(jz)) + / static_cast(MZ); +} + +BoutReal BoutMesh::GlobalZ(BoutReal jz) const { + + // Get global Z index as a BoutReal + const BoutReal zglo = getGlobalZIndex(jz); + + if (symmetricGlobalZ) { + // With this definition the boundary sits dz/2 away form the first/last inner points + return (0.5 + zglo - (nz - MZ) * 0.5) / static_cast(MZ); + } + return zglo / static_cast(MZ); +} + void BoutMesh::outputVars(Options& output_options) { Timer time("io"); output_options["zperiod"].force(zperiod, "BoutMesh"); diff --git a/src/mesh/impls/bout/boutmesh.hxx b/src/mesh/impls/bout/boutmesh.hxx index cc674d401a..dfec3342ed 100644 --- a/src/mesh/impls/bout/boutmesh.hxx +++ b/src/mesh/impls/bout/boutmesh.hxx @@ -4,15 +4,20 @@ #include "mpi.h" +#include "bout/bout_types.hxx" +#include "bout/field_data.hxx" #include "bout/unused.hxx" #include -#include +#include #include +#include #include #include #include +class Field; + /// Implementation of Mesh (mostly) compatible with BOUT /// /// Topology and communications compatible with BOUT @@ -58,10 +63,13 @@ public: ///////////////////////////////////////////// // non-local communications - int getNXPE() override; ///< The number of processors in the X direction - int getNYPE() override; ///< The number of processors in the Y direction - int getXProcIndex() override; ///< This processor's index in X direction - int getYProcIndex() override; ///< This processor's index in Y direction + int getNXPE() const override; ///< The number of processors in the X direction + int getNYPE() const override; ///< The number of processors in the Y direction + int getNZPE() const override; ///< The number of processors in the Z direction + int getXProcIndex() const override; ///< This processor's index in X direction + int getYProcIndex() const override; ///< This processor's index in Y direction + int getZProcIndex() const override; ///< This processor's index in Z direction + int getProcIndex(int X, int Y, int Z) const override; ///////////////////////////////////////////// // X communications @@ -105,6 +113,7 @@ public: MPI_Comm getXcomm(int UNUSED(jy)) const override { return comm_x; } /// Return communicator containing all processors in Y MPI_Comm getYcomm(int xpos) const override; + MPI_Comm getXZcomm() const override { return comm_xz; } /// Is local X index \p jx periodic in Y? /// @@ -156,23 +165,26 @@ public: RangeIterator iterateBndryUpperInnerY() const override; RangeIterator iterateBndryUpperOuterY() const override; + bool hasBndryLowerY() const override { return has_boundary_lower_y; } + bool hasBndryUpperY() const override { return has_boundary_upper_y; } + // Boundary regions - std::vector getBoundaries() override; - std::vector> - getBoundariesPar(BoundaryParType type) override; - void addBoundaryPar(std::shared_ptr bndry, + std::vector getBoundaries() override; + std::vector> + getBoundariesPar(BoundaryParType type) const override; + void addBoundaryPar(std::shared_ptr bndry, BoundaryParType type) override; std::set getPossibleBoundaries() const override; - Field3D smoothSeparatrix(const Field3D& f) override; - int getNx() const { return nx; } int getNy() const { return ny; } BoutReal GlobalX(int jx) const override; BoutReal GlobalY(int jy) const override; + BoutReal GlobalZ(int jz) const override; BoutReal GlobalX(BoutReal jx) const override; BoutReal GlobalY(BoutReal jy) const override; + BoutReal GlobalZ(BoutReal jz) const override; BoutReal getIxseps1() const { return ixseps1; } BoutReal getIxseps2() const { return ixseps2; } @@ -205,7 +217,7 @@ protected: /// `getPossibleBoundaries`. \p create_regions controls whether or /// not the various `Region`s are created on the new mesh BoutMesh(int input_nx, int input_ny, int input_nz, int mxg, int myg, int nxpe, int nype, - int pe_xind, int pe_yind, bool symmetric_X, bool symmetric_Y, bool periodic_X, + int pe_xind, int pe_yind, bool symmetric_X, bool symmetric_Y, bool periodic_X_, int ixseps1_, int ixseps2_, int jyseps1_1_, int jyseps2_1_, int jyseps1_2_, int jyseps2_2_, int ny_inner_, bool create_regions = true); @@ -294,16 +306,24 @@ private: int NPES; ///< Number of processors int MYPE; ///< Rank of this processor + int PE_XIND; ///< X index of this processor + int NXPE; ///< Number of processors in the X direction + int PE_YIND; ///< Y index of this processor - int NYPE; // Number of processors in the Y direction + int NYPE; ///< Number of processors in the Y direction - int NZPE; + int PE_ZIND{0}; ///< Z index of this processor + int NZPE{1}; ///< Number of processors in the Z direction /// Is this processor in the core region? bool MYPE_IN_CORE{false}; - int XGLOBAL(BoutReal xloc, BoutReal& xglo) const; - int YGLOBAL(BoutReal yloc, BoutReal& yglo) const; + /// Returns the global X index given a local index + BoutReal getGlobalXIndex(BoutReal xloc) const; + /// Returns the global Y index given a local index + BoutReal getGlobalYIndex(BoutReal yloc) const; + /// Returns the global Z index given a local index + BoutReal getGlobalZIndex(BoutReal zloc) const; // Topology int ixseps1, ixseps2, jyseps1_1, jyseps2_1, jyseps1_2, jyseps2_2; @@ -354,8 +374,9 @@ private: // Settings bool TwistShift; // Use a twist-shift condition in core? - bool symmetricGlobalX; ///< Use a symmetric definition in GlobalX() function - bool symmetricGlobalY; + bool symmetricGlobalX; ///< Use a symmetric definition in `GlobalX()` function + bool symmetricGlobalY; ///< Use a symmetric definition in `GlobalY()` function + bool symmetricGlobalZ{false}; ///< Use a symmetric definition in `GlobalZ()` function int zperiod; BoutReal ZMIN, ZMAX; // Range of the Z domain (in fractions of 2pi) @@ -395,11 +416,13 @@ protected: void addBoundaryRegions(); private: - std::vector boundary; // Vector of boundary regions - std::array>, + std::vector boundary; // Vector of boundary regions + std::array>, static_cast(BoundaryParType::SIZE)> par_boundary; // Vector of parallel boundary regions + bool has_boundary_lower_y{false}; + bool has_boundary_upper_y{false}; ////////////////////////////////////////////////// // Communications @@ -439,6 +462,8 @@ private: /// Communicator containing all processors in X MPI_Comm comm_x{MPI_COMM_NULL}; + /// Communicator for all processors in an XZ plane + MPI_Comm comm_xz{MPI_COMM_NULL}; ////////////////////////////////////////////////// // Surface communications @@ -460,12 +485,11 @@ private: void post_receiveY(CommHandle& ch); /// Take data from objects and put into a buffer - int pack_data(const std::vector& var_list, int xge, int xlt, int yge, - int ylt, BoutReal* buffer); + int pack_data(const std::vector& var_list, int xge, int xlt, int yge, int ylt, + BoutReal* buffer) const; /// Copy data from a buffer back into the fields - - int unpack_data(const std::vector& var_list, int xge, int xlt, int yge, - int ylt, BoutReal* buffer); + int unpack_data(const std::vector& var_list, int xge, int xlt, int yge, int ylt, + const BoutReal* buffer) const; }; namespace { diff --git a/src/mesh/index_derivs.cxx b/src/mesh/index_derivs.cxx index add67a77c8..70fc47b538 100644 --- a/src/mesh/index_derivs.cxx +++ b/src/mesh/index_derivs.cxx @@ -25,7 +25,6 @@ #include "bout/traits.hxx" #include #include -#include #include /******************************************************************************* @@ -34,7 +33,6 @@ /// Initialise the derivative methods. Must be called before any derivatives are used void Mesh::derivs_init(Options* options) { - TRACE("Initialising derivatives"); // For each direction need to set what the default method is for each type // of derivative. DerivativeStore::getInstance().initialise(options); @@ -45,7 +43,6 @@ void Mesh::derivs_init(Options* options) { STAGGER Mesh::getStagger(const CELL_LOC inloc, const CELL_LOC outloc, const CELL_LOC allowedStaggerLoc) const { - TRACE("Mesh::getStagger -- three arguments"); ASSERT1(outloc == inloc || (outloc == CELL_CENTRE && inloc == allowedStaggerLoc) || (outloc == allowedStaggerLoc && inloc == CELL_CENTRE)); @@ -61,7 +58,6 @@ STAGGER Mesh::getStagger(const CELL_LOC inloc, const CELL_LOC outloc, STAGGER Mesh::getStagger(const CELL_LOC vloc, [[maybe_unused]] const CELL_LOC inloc, const CELL_LOC outloc, const CELL_LOC allowedStaggerLoc) const { - TRACE("Mesh::getStagger -- four arguments"); ASSERT1(inloc == outloc); ASSERT1(vloc == inloc || (vloc == CELL_CENTRE && inloc == allowedStaggerLoc) || (vloc == allowedStaggerLoc && inloc == CELL_CENTRE)); @@ -423,7 +419,7 @@ class FFTDerivativeType { public: template void standard(const T& var, T& result, const std::string& region) const { - AUTO_TRACE(); + ASSERT2(meta.derivType == DERIV::Standard) ASSERT2(var.getMesh()->getNguard(direction) >= nGuards); ASSERT2(direction == DIRECTION::Z); // Only in Z for now @@ -431,6 +427,7 @@ class FFTDerivativeType { ASSERT2(bout::utils::is_Field3D_v); // Should never need to call this with Field2D auto* theMesh = var.getMesh(); + ASSERT2(theMesh->getNZPE() == 1); // Only works if serial in Z for FFTs // Calculate how many Z wavenumbers will be removed const int ncz = theMesh->getNpoints(direction); @@ -479,7 +476,7 @@ class FFTDerivativeType { template void upwindOrFlux(const T& UNUSED(vel), const T& UNUSED(var), T& UNUSED(result), const std::string& UNUSED(region)) const { - AUTO_TRACE(); + throw BoutException("The FFT METHOD isn't available in upwind/Flux"); } static constexpr metaData meta{"FFT", 0, DERIV::Standard}; @@ -489,7 +486,7 @@ class FFT2ndDerivativeType { public: template void standard(const T& var, T& result, const std::string& region) const { - AUTO_TRACE(); + ASSERT2(meta.derivType == DERIV::StandardSecond); ASSERT2(var.getMesh()->getNguard(direction) >= nGuards); ASSERT2(direction == DIRECTION::Z); // Only in Z for now @@ -497,6 +494,7 @@ class FFT2ndDerivativeType { ASSERT2(bout::utils::is_Field3D_v); // Should never need to call this with Field2D auto* theMesh = var.getMesh(); + ASSERT2(theMesh->getNZPE() == 1); // Only works if serial in Z for FFTs // Calculate how many Z wavenumbers will be removed const int ncz = theMesh->getNpoints(direction); @@ -536,7 +534,7 @@ class FFT2ndDerivativeType { template void upwindOrFlux(const T& UNUSED(vel), const T& UNUSED(var), T& UNUSED(result), const std::string& UNUSED(region)) const { - AUTO_TRACE(); + throw BoutException("The FFT METHOD isn't available in upwind/Flux"); } static constexpr metaData meta{"FFT", 0, DERIV::StandardSecond}; @@ -552,14 +550,14 @@ class SplitFluxDerivativeType { public: template void standard(const T&, T&, const std::string) const { - AUTO_TRACE(); + throw BoutException("The SPLIT method isn't available for standard"); } template void upwindOrFlux(const T& vel, const T& var, T& result, const std::string region) const { - AUTO_TRACE(); + // Split into an upwind and a central differencing part // d/dx(v*f) = v*d/dx(f) + f*d/dx(v) result = bout::derivatives::index::flowDerivative( diff --git a/src/mesh/interpolation/bilinear_xz.cxx b/src/mesh/interpolation/bilinear_xz.cxx index 8445764a8f..1ed18a7303 100644 --- a/src/mesh/interpolation/bilinear_xz.cxx +++ b/src/mesh/interpolation/bilinear_xz.cxx @@ -31,6 +31,10 @@ XZBilinear::XZBilinear(int y_offset, Mesh* mesh) : XZInterpolation(y_offset, mesh), w0(localmesh), w1(localmesh), w2(localmesh), w3(localmesh) { + if (localmesh->getNXPE() > 1) { + throw BoutException("XZBilinear interpolation does not support MPI splitting in X"); + } + // Index arrays contain guard cells in order to get subscripts right i_corner.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); k_corner.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index a8e5df7cdf..afbd336bb2 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -1,7 +1,7 @@ /************************************************************************** - * Copyright 2015-2018 B.D.Dudson, P. Hill + * Copyright 2015 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -20,25 +20,116 @@ * **************************************************************************/ +#include "../impls/bout/boutmesh.hxx" +#include "../parallel/fci_comm.hxx" +#include "bout/bout.hxx" +#include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" #include "bout/globals.hxx" #include "bout/index_derivs_interface.hxx" #include "bout/interpolation_xz.hxx" -#include "bout/mesh.hxx" +#include "bout/mask.hxx" +#include +#include +#include #include -XZHermiteSpline::XZHermiteSpline(int y_offset, Mesh* mesh) - : XZInterpolation(y_offset, mesh), h00_x(localmesh), h01_x(localmesh), +class IndConverter { +public: + IndConverter(Mesh* mesh) + : mesh(dynamic_cast(mesh)), nxpe(mesh->getNXPE()), nype(mesh->getNYPE()), + xstart(mesh->xstart), ystart(mesh->ystart), zstart(0), + lnx(mesh->LocalNx - 2 * xstart), lny(mesh->LocalNy - 2 * ystart), + lnz(mesh->LocalNz - 2 * zstart) {} + // ix and iz are global indices + // iy is local + int fromMeshToGlobal(int ix, int iy, int iz) const { + const int xstart = mesh->xstart; + const int lnx = mesh->LocalNx - xstart * 2; + // x-proc-id + int pex = divToNeg(ix - xstart, lnx); + if (pex < 0) { + pex = 0; + } + if (pex >= nxpe) { + pex = nxpe - 1; + } + const int zstart = 0; + const int lnz = mesh->LocalNz - zstart * 2; + // z-proc-id + // pez only for wrapping around ; later needs similar treatment than pey + const int pez = divToNeg(iz - zstart, lnz); + // y proc-id - y is already local + const int ystart = mesh->ystart; + const int lny = mesh->LocalNy - ystart * 2; + const int pey_offset = divToNeg(iy - ystart, lny); + int pey = pey_offset + mesh->getYProcIndex(); + while (pey < 0) { + pey += nype; + } + while (pey >= nype) { + pey -= nype; + } + ASSERT2(pex >= 0); + ASSERT2(pex < nxpe); + ASSERT2(pey >= 0); + ASSERT2(pey < nype); + return fromLocalToGlobal(ix - pex * lnx, iy - pey_offset * lny, iz - pez * lnz, pex, + pey, 0); + } + int fromLocalToGlobal(const int ilocalx, const int ilocaly, const int ilocalz) const { + return fromLocalToGlobal(ilocalx, ilocaly, ilocalz, mesh->getXProcIndex(), + mesh->getYProcIndex(), 0); + } + int fromLocalToGlobal(const int ilocalx, const int ilocaly, const int ilocalz, + const int pex, const int pey, const int pez) const { + ASSERT3(ilocalx >= 0); + ASSERT3(ilocaly >= 0); + ASSERT3(ilocalz >= 0); + const int ilocal = ((ilocalx * mesh->LocalNy) + ilocaly) * mesh->LocalNz + ilocalz; + const int ret = ilocal + + mesh->LocalNx * mesh->LocalNy * mesh->LocalNz + * ((pey * nxpe + pex) * nzpe + pez); + ASSERT3(ret >= 0); + ASSERT3(ret < nxpe * nype * mesh->LocalNx * mesh->LocalNy * mesh->LocalNz); + return ret; + } + +private: + // number of procs + BoutMesh* mesh; + const int nxpe; + const int nype; + const int nzpe{1}; + const int xstart, ystart, zstart; + const int lnx, lny, lnz; + static int divToNeg(const int n, const int d) { + return (n < 0) ? ((n - d + 1) / d) : (n / d); + } +}; + +template +XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh* meshin, + Options* options) + : XZInterpolation(y_offset, meshin), h00_x(localmesh), h01_x(localmesh), h10_x(localmesh), h11_x(localmesh), h00_z(localmesh), h01_z(localmesh), h10_z(localmesh), h11_z(localmesh) { + if constexpr (monotonic) { + if (options == nullptr) { + options = &Options::root()["mesh:paralleltransform:xzinterpolation"]; + } + abs_fac_monotonic = (*options)["abs_tol"].withDefault(abs_fac_monotonic); + rel_fac_monotonic = (*options)["rel_tol"].withDefault(rel_fac_monotonic); + } + // Index arrays contain guard cells in order to get subscripts right i_corner.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); k_corner.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); // Initialise in order to avoid 'uninitialized value' errors from Valgrind when using // guard-cell values - i_corner = -1; k_corner = -1; // Allocate Field3D members @@ -50,45 +141,97 @@ XZHermiteSpline::XZHermiteSpline(int y_offset, Mesh* mesh) h01_z.allocate(); h10_z.allocate(); h11_z.allocate(); -} -void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z, - const std::string& region) { + if constexpr (imp_type == bout::details::implementation_type::new_weights + || imp_type == bout::details::implementation_type::petsc) { + newWeights.reserve(16); + for (int w = 0; w < 16; ++w) { + newWeights.emplace_back(localmesh); + newWeights[w].allocate(); + } + } + if constexpr (imp_type == bout::details::implementation_type::petsc) { +#if BOUT_HAS_PETSC + petsclib = new PetscLib( + &Options::root()["mesh:paralleltransform:xzinterpolation:hermitespline"]); + const int m = localmesh->LocalNx * localmesh->LocalNy * localmesh->LocalNz; + const int M = m * localmesh->getNXPE() * localmesh->getNYPE() * localmesh->getNZPE(); + MatCreateAIJ(BoutComm::get(), m, m, M, M, 16, nullptr, 16, nullptr, &petscWeights); +#endif + } + if constexpr (monotonic) { + gf3daccess = std::make_unique(localmesh); + g3dinds.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); + } + if constexpr (imp_type == bout::details::implementation_type::new_weights + || imp_type == bout::details::implementation_type::legacy) { + if (localmesh->getNXPE() > 1) { + throw BoutException("Require PETSc for MPI splitting in X"); + } + } +} - const int ncz = localmesh->LocalNz; - const auto curregion{getRegion(region)}; - BOUT_FOR(i, curregion) { +template +void XZHermiteSplineBase::calcWeights( + const Field3D& delta_x, const Field3D& delta_z, + [[maybe_unused]] const std::string& region) { + + const int ny = localmesh->LocalNy; + const int nz = localmesh->LocalNz; + const int xend = (localmesh->xend - localmesh->xstart + 1) * localmesh->getNXPE() + + localmesh->xstart - 1; + [[maybe_unused]] const IndConverter conv{localmesh}; + + [[maybe_unused]] const int y_global_offset = + localmesh->getYProcIndex() * (localmesh->yend - localmesh->ystart + 1); +#if BOUT_HAS_PETSC + BOUT_FOR_SERIAL(i, getRegion(region)) { +#else + BOUT_FOR(i, getRegion(region)) { +#endif const int x = i.x(); const int y = i.y(); const int z = i.z(); // The integer part of xt_prime, zt_prime are the indices of the cell // containing the field line end-point - i_corner(x, y, z) = static_cast(floor(delta_x(x, y, z))); + int i_corn = static_cast(floor(delta_x(x, y, z))); k_corner(x, y, z) = static_cast(floor(delta_z(x, y, z))); // t_x, t_z are the normalised coordinates \in [0,1) within the cell // calculated by taking the remainder of the floating point index - BoutReal t_x = delta_x(x, y, z) - static_cast(i_corner(x, y, z)); + BoutReal t_x = delta_x(x, y, z) - static_cast(i_corn); BoutReal t_z = delta_z(x, y, z) - static_cast(k_corner(x, y, z)); - // NOTE: A (small) hack to avoid one-sided differences - if (i_corner(x, y, z) >= localmesh->xend) { - i_corner(x, y, z) = localmesh->xend - 1; + // NOTE: A (small) hack to avoid one-sided differences. We need at + // least 2 interior points due to an awkwardness with the + // boundaries. The splines need derivatives in x, but we don't + // know the value in the boundaries, so _any_ interpolation in the + // last interior cell can't be done. Instead, we fudge the + // interpolation in the last cell to be at the extreme right-hand + // edge of the previous cell (that is, exactly on the last + // interior point). However, this doesn't work with only one + // interior point, because we have to do something similar to the + // _first_ cell, and these two fudges cancel out and we end up + // indexing into the boundary anyway. + // TODO(peter): Can we remove this if we apply (dirichlet?) BCs to + // the X derivatives? Note that we need at least _2_ + if (i_corn >= xend) { + i_corn = xend - 1; t_x = 1.0; } - if (i_corner(x, y, z) < localmesh->xstart) { - i_corner(x, y, z) = localmesh->xstart; + if (i_corn < localmesh->xstart) { + i_corn = localmesh->xstart; t_x = 0.0; } - k_corner(x, y, z) = ((k_corner(x, y, z) % ncz) + ncz) % ncz; + k_corner(x, y, z) = ((k_corner(x, y, z) % nz) + nz) % nz; // Check that t_x and t_z are in range if ((t_x < 0.0) || (t_x > 1.0)) { throw BoutException( - "t_x={:e} out of range at ({:d},{:d},{:d}) (delta_x={:e}, i_corner={:d})", t_x, - x, y, z, delta_x(x, y, z), i_corner(x, y, z)); + "t_x={:e} out of range at ({:d},{:d},{:d}) (delta_x={:e}, i_corn={:d})", t_x, x, + y, z, delta_x(x, y, z), i_corn); } if ((t_z < 0.0) || (t_z > 1.0)) { @@ -97,22 +240,127 @@ void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z x, y, z, delta_z(x, y, z), k_corner(x, y, z)); } - h00_x(x, y, z) = (2. * t_x * t_x * t_x) - (3. * t_x * t_x) + 1.; - h00_z(x, y, z) = (2. * t_z * t_z * t_z) - (3. * t_z * t_z) + 1.; - - h01_x(x, y, z) = (-2. * t_x * t_x * t_x) + (3. * t_x * t_x); - h01_z(x, y, z) = (-2. * t_z * t_z * t_z) + (3. * t_z * t_z); - - h10_x(x, y, z) = t_x * (1. - t_x) * (1. - t_x); - h10_z(x, y, z) = t_z * (1. - t_z) * (1. - t_z); - - h11_x(x, y, z) = (t_x * t_x * t_x) - (t_x * t_x); - h11_z(x, y, z) = (t_z * t_z * t_z) - (t_z * t_z); + i_corner[i] = SpecificInd( + (((i_corn * ny) + (y + y_offset)) * nz + k_corner(x, y, z)), ny, nz); + + h00_x[i] = (2. * t_x * t_x * t_x) - (3. * t_x * t_x) + 1.; + h00_z[i] = (2. * t_z * t_z * t_z) - (3. * t_z * t_z) + 1.; + + h01_x[i] = (-2. * t_x * t_x * t_x) + (3. * t_x * t_x); + h01_z[i] = (-2. * t_z * t_z * t_z) + (3. * t_z * t_z); + + h10_x[i] = t_x * (1. - t_x) * (1. - t_x); + h10_z[i] = t_z * (1. - t_z) * (1. - t_z); + + h11_x[i] = (t_x * t_x * t_x) - (t_x * t_x); + h11_z[i] = (t_z * t_z * t_z) - (t_z * t_z); + + if constexpr (imp_type == bout::details::implementation_type::new_weights + || imp_type == bout::details::implementation_type::petsc) { + for (int w = 0; w < 16; ++w) { + newWeights[w][i] = 0; + } + // The distribution of our weights: + // 0 4 8 12 + // 1 5 9 13 + // 2 6 10 14 + // 3 7 11 15 + // e.g. 1 == ic.xm(); 4 == ic.zm(); 5 == ic; 7 == ic.zp(2); + + // f[ic] * h00_x[i] + f[icxp] * h01_x[i] + fx[ic] * h10_x[i] + fx[icxp] * h11_x[i]; + newWeights[5][i] += h00_x[i] * h00_z[i]; + newWeights[9][i] += h01_x[i] * h00_z[i]; + newWeights[9][i] += h10_x[i] * h00_z[i] / 2; + newWeights[1][i] -= h10_x[i] * h00_z[i] / 2; + newWeights[13][i] += h11_x[i] * h00_z[i] / 2; + newWeights[5][i] -= h11_x[i] * h00_z[i] / 2; + + // f[iczp] * h00_x[i] + f[icxpzp] * h01_x[i] + + // fx[iczp] * h10_x[i] + fx[icxpzp] * h11_x[i]; + newWeights[6][i] += h00_x[i] * h01_z[i]; + newWeights[10][i] += h01_x[i] * h01_z[i]; + newWeights[10][i] += h10_x[i] * h01_z[i] / 2; + newWeights[2][i] -= h10_x[i] * h01_z[i] / 2; + newWeights[14][i] += h11_x[i] * h01_z[i] / 2; + newWeights[6][i] -= h11_x[i] * h01_z[i] / 2; + + // fz[ic] * h00_x[i] + fz[icxp] * h01_x[i] + + // fxz[ic] * h10_x[i]+ fxz[icxp] * h11_x[i]; + newWeights[6][i] += h00_x[i] * h10_z[i] / 2; + newWeights[4][i] -= h00_x[i] * h10_z[i] / 2; + newWeights[10][i] += h01_x[i] * h10_z[i] / 2; + newWeights[8][i] -= h01_x[i] * h10_z[i] / 2; + newWeights[10][i] += h10_x[i] * h10_z[i] / 4; + newWeights[8][i] -= h10_x[i] * h10_z[i] / 4; + newWeights[2][i] -= h10_x[i] * h10_z[i] / 4; + newWeights[0][i] += h10_x[i] * h10_z[i] / 4; + newWeights[14][i] += h11_x[i] * h10_z[i] / 4; + newWeights[12][i] -= h11_x[i] * h10_z[i] / 4; + newWeights[6][i] -= h11_x[i] * h10_z[i] / 4; + newWeights[4][i] += h11_x[i] * h10_z[i] / 4; + + // fz[iczp] * h00_x[i] + fz[icxpzp] * h01_x[i] + + // fxz[iczp] * h10_x[i] + fxz[icxpzp] * h11_x[i]; + newWeights[7][i] += h00_x[i] * h11_z[i] / 2; + newWeights[5][i] -= h00_x[i] * h11_z[i] / 2; + newWeights[11][i] += h01_x[i] * h11_z[i] / 2; + newWeights[9][i] -= h01_x[i] * h11_z[i] / 2; + newWeights[11][i] += h10_x[i] * h11_z[i] / 4; + newWeights[9][i] -= h10_x[i] * h11_z[i] / 4; + newWeights[3][i] -= h10_x[i] * h11_z[i] / 4; + newWeights[1][i] += h10_x[i] * h11_z[i] / 4; + newWeights[15][i] += h11_x[i] * h11_z[i] / 4; + newWeights[13][i] -= h11_x[i] * h11_z[i] / 4; + newWeights[7][i] -= h11_x[i] * h11_z[i] / 4; + newWeights[5][i] += h11_x[i] * h11_z[i] / 4; + if (imp_type == bout::details::implementation_type::petsc) { +#if BOUT_HAS_PETSC + PetscInt idxn[1] = {conv.fromLocalToGlobal(x, y + y_offset, z)}; + // output.write("debug: {:d} -> {:d}: {:d}:{:d} -> {:d}:{:d}\n", + // conv.fromLocalToGlobal(x, y + y_offset, z), + // conv.fromMeshToGlobal(i_corn, y + y_offset, k_corner(x, y, z)), + // x, z, i_corn, k_corner(x, y, z)); + // ixstep = mesh->LocalNx * mesh->LocalNz; + for (int j = 0; j < 4; ++j) { + PetscInt idxm[4]; + PetscScalar vals[4]; + for (int k = 0; k < 4; ++k) { + idxm[k] = conv.fromMeshToGlobal(i_corn - 1 + j, y + y_offset, + k_corner(x, y, z) - 1 + k); + vals[k] = newWeights[(j * 4) + k][i]; + } + MatSetValues(petscWeights, 1, idxn, 4, idxm, vals, INSERT_VALUES); + } +#endif + } + } + if constexpr (monotonic) { + const auto gind = gf3daccess->xyzglobal(i_corn, y + y_offset + y_global_offset, + k_corner(x, y, z)); + gf3daccess->request(gind); + gf3daccess->request(gind.xp(1)); + gf3daccess->request(gind.zp(1)); + gf3daccess->request(gind.xp(1).zp(1)); + g3dinds[i] = {gind.ind, gind.xp(1).ind, gind.zp(1).ind, gind.xp(1).zp(1).ind}; + } + } + if constexpr (imp_type == bout::details::implementation_type::petsc) { +#if BOUT_HAS_PETSC + MatAssemblyBegin(petscWeights, MAT_FINAL_ASSEMBLY); + MatAssemblyEnd(petscWeights, MAT_FINAL_ASSEMBLY); + if (!isInit) { + MatCreateVecs(petscWeights, &rhs, &result); + } + isInit = true; +#endif } } -void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z, - const BoutMask& mask, const std::string& region) { +template +void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, + const Field3D& delta_z, + const BoutMask& mask, + const std::string& region) { setMask(mask); calcWeights(delta_x, delta_z, region); } @@ -133,13 +381,19 @@ void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z * (i, j+1, k+1) h01_z + h10_z / 2 * (i, j+1, k+2) h11_z / 2 */ +template std::vector -XZHermiteSpline::getWeightsForYApproximation(int i, int j, int k, int yoffset) { - const int ncz = localmesh->LocalNz; +XZHermiteSplineBase::getWeightsForYApproximation(int i, int j, int k, + int yoffset) { + if (localmesh->getNXPE() > 1) { + throw BoutException("It is likely that the function calling this is not handling the " + "result correctly."); + } + const int nz = localmesh->LocalNz; const int k_mod = k_corner(i, j, k); - const int k_mod_m1 = (k_mod > 0) ? (k_mod - 1) : (ncz - 1); - const int k_mod_p1 = (k_mod + 1) % ncz; - const int k_mod_p2 = (k_mod + 2) % ncz; + const int k_mod_m1 = (k_mod > 0) ? (k_mod - 1) : (nz - 1); + const int k_mod_p1 = (k_mod == nz) ? 0 : k_mod + 1; + const int k_mod_p2 = (k_mod_p1 == nz) ? 0 : k_mod_p1 + 1; return {{i, j + yoffset, k_mod_m1, -0.5 * h10_z(i, j, k)}, {i, j + yoffset, k_mod, h00_z(i, j, k) - 0.5 * h11_z(i, j, k)}, @@ -147,92 +401,158 @@ XZHermiteSpline::getWeightsForYApproximation(int i, int j, int k, int yoffset) { {i, j + yoffset, k_mod_p2, 0.5 * h11_z(i, j, k)}}; } -Field3D XZHermiteSpline::interpolate(const Field3D& f, const std::string& region) const { +template +Field3D XZHermiteSplineBase::interpolate( + const Field3D& f, [[maybe_unused]] const std::string& region) const { ASSERT1(f.getMesh() == localmesh); Field3D f_interp{emptyFrom(f)}; - // Derivatives are used for tension and need to be on dimensionless - // coordinates - Field3D fx = bout::derivatives::index::DDX(f, CELL_DEFAULT, "DEFAULT"); - localmesh->communicateXZ(fx); - // communicate in y, but do not calculate parallel slices - { - auto h = localmesh->sendY(fx); - localmesh->wait(h); - } - Field3D fz = bout::derivatives::index::DDZ(f, CELL_DEFAULT, "DEFAULT", "RGN_ALL"); - localmesh->communicateXZ(fz); - // communicate in y, but do not calculate parallel slices - { - auto h = localmesh->sendY(fz); - localmesh->wait(h); - } - Field3D fxz = bout::derivatives::index::DDX(fz, CELL_DEFAULT, "DEFAULT"); - localmesh->communicateXZ(fxz); - // communicate in y, but do not calculate parallel slices - { - auto h = localmesh->sendY(fxz); - localmesh->wait(h); - } - - const auto curregion{getRegion(region)}; - BOUT_FOR(i, curregion) { - const int x = i.x(); - const int y = i.y(); - const int z = i.z(); + const auto region2 = y_offset != 0 ? fmt::format("RGN_YPAR_{:+d}", y_offset) : region; + + std::unique_ptr gf; + if constexpr (monotonic) { + gf = gf3daccess->communicate_asPtr(f); + } + + if constexpr (imp_type == bout::details::implementation_type::petsc) { +#if BOUT_HAS_PETSC + BoutReal* ptr = nullptr; + const BoutReal* cptr = nullptr; + VecGetArray(rhs, &ptr); + BOUT_FOR(i, f.getRegion("RGN_NOY")) { ptr[int(i)] = f[i]; } + VecRestoreArray(rhs, &ptr); + MatMult(petscWeights, rhs, result); + VecGetArrayRead(result, &cptr); + BOUT_FOR(i, f.getRegion(region2)) { + f_interp[i] = cptr[int(i)]; + if constexpr (monotonic) { + const auto iyp = i; + const auto i = iyp.ym(y_offset); + const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], + (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; + const auto minmax = std::minmax(corners); + const auto diff = + ((minmax.second - minmax.first) * rel_fac_monotonic) + abs_fac_monotonic; + f_interp[iyp] = std::max(f_interp[iyp], minmax.first - diff); + f_interp[iyp] = std::min(f_interp[iyp], minmax.second + diff); + } + ASSERT2(std::isfinite(cptr[int(i)])); + } + VecRestoreArrayRead(result, &cptr); +#endif + } - // Due to lack of guard cells in z-direction, we need to ensure z-index - // wraps around - const int z_mod = k_corner(x, y, z); - const int z_mod_p1 = (z_mod + 1) % localmesh->LocalNz; - - const int y_next = y + y_offset; - - // Interpolate f in X at Z - const BoutReal f_z = f(i_corner(x, y, z), y_next, z_mod) * h00_x(x, y, z) - + f(i_corner(x, y, z) + 1, y_next, z_mod) * h01_x(x, y, z) - + fx(i_corner(x, y, z), y_next, z_mod) * h10_x(x, y, z) - + fx(i_corner(x, y, z) + 1, y_next, z_mod) * h11_x(x, y, z); - - // Interpolate f in X at Z+1 - const BoutReal f_zp1 = f(i_corner(x, y, z), y_next, z_mod_p1) * h00_x(x, y, z) - + f(i_corner(x, y, z) + 1, y_next, z_mod_p1) * h01_x(x, y, z) - + fx(i_corner(x, y, z), y_next, z_mod_p1) * h10_x(x, y, z) - + fx(i_corner(x, y, z) + 1, y_next, z_mod_p1) * h11_x(x, y, z); - - // Interpolate fz in X at Z - const BoutReal fz_z = fz(i_corner(x, y, z), y_next, z_mod) * h00_x(x, y, z) - + fz(i_corner(x, y, z) + 1, y_next, z_mod) * h01_x(x, y, z) - + fxz(i_corner(x, y, z), y_next, z_mod) * h10_x(x, y, z) - + fxz(i_corner(x, y, z) + 1, y_next, z_mod) * h11_x(x, y, z); - - // Interpolate fz in X at Z+1 - const BoutReal fz_zp1 = - fz(i_corner(x, y, z), y_next, z_mod_p1) * h00_x(x, y, z) - + fz(i_corner(x, y, z) + 1, y_next, z_mod_p1) * h01_x(x, y, z) - + fxz(i_corner(x, y, z), y_next, z_mod_p1) * h10_x(x, y, z) - + fxz(i_corner(x, y, z) + 1, y_next, z_mod_p1) * h11_x(x, y, z); - - // Interpolate in Z - f_interp(x, y_next, z) = +f_z * h00_z(x, y, z) + f_zp1 * h01_z(x, y, z) - + fz_z * h10_z(x, y, z) + fz_zp1 * h11_z(x, y, z); - - ASSERT2(std::isfinite(f_interp(x, y_next, z)) || x < localmesh->xstart - || x > localmesh->xend); + if constexpr (imp_type == bout::details::implementation_type::new_weights) { + BOUT_FOR(i, getRegion(region)) { + auto ic = i_corner[i]; + auto iyp = i.yp(y_offset); + + f_interp[iyp] = 0; + for (int w = 0; w < 4; ++w) { + f_interp[iyp] += newWeights[(w * 4) + 0][i] * f[ic.zm().xp(w - 1)]; + f_interp[iyp] += newWeights[(w * 4) + 1][i] * f[ic.xp(w - 1)]; + f_interp[iyp] += newWeights[(w * 4) + 2][i] * f[ic.zp().xp(w - 1)]; + f_interp[iyp] += newWeights[(w * 4) + 3][i] * f[ic.zp(2).xp(w - 1)]; + } + if constexpr (monotonic) { + const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], + (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; + const auto minmax = std::minmax(corners); + const auto diff = + ((minmax.second - minmax.first) * rel_fac_monotonic) + abs_fac_monotonic; + f_interp[iyp] = std::max(f_interp[iyp], minmax.first - diff); + f_interp[iyp] = std::min(f_interp[iyp], minmax.second + diff); + } + ASSERT2(std::isfinite(f_interp[iyp])); + } } + if constexpr (imp_type == bout::details::implementation_type::legacy) { + // Legacy interpolation + // TODO(peter): Should we apply dirichlet BCs to derivatives? + // Derivatives are used for tension and need to be on dimensionless + // coordinates + + // f has been communcated, and thus we can assume that the x-boundaries are + // also valid in the y-boundary. Thus the differentiated field needs no + // extra comms. + // TODO(dave) Add assert that we do not use z-splitting or z-guards. + Field3D fx = bout::derivatives::index::DDX(f, CELL_DEFAULT, "DEFAULT", region2); + Field3D fz = bout::derivatives::index::DDZ(f, CELL_DEFAULT, "DEFAULT", region2); + Field3D fxz = bout::derivatives::index::DDZ(fx, CELL_DEFAULT, "DEFAULT", region2); + + BOUT_FOR(i, getRegion(region)) { + const auto iyp = i.yp(y_offset); + + const auto ic = i_corner[i]; + const auto iczp = ic.zp(); + const auto icxp = ic.xp(); + const auto icxpzp = iczp.xp(); + + // Interpolate f in X at Z + const BoutReal f_z = + f[ic] * h00_x[i] + f[icxp] * h01_x[i] + fx[ic] * h10_x[i] + fx[icxp] * h11_x[i]; + + // Interpolate f in X at Z+1 + const BoutReal f_zp1 = f[iczp] * h00_x[i] + f[icxpzp] * h01_x[i] + + fx[iczp] * h10_x[i] + fx[icxpzp] * h11_x[i]; + + // Interpolate fz in X at Z + const BoutReal fz_z = fz[ic] * h00_x[i] + fz[icxp] * h01_x[i] + fxz[ic] * h10_x[i] + + fxz[icxp] * h11_x[i]; + + // Interpolate fz in X at Z+1 + const BoutReal fz_zp1 = fz[iczp] * h00_x[i] + fz[icxpzp] * h01_x[i] + + fxz[iczp] * h10_x[i] + fxz[icxpzp] * h11_x[i]; + + // Interpolate in Z + f_interp[iyp] = + +f_z * h00_z[i] + f_zp1 * h01_z[i] + fz_z * h10_z[i] + fz_zp1 * h11_z[i]; + + if constexpr (monotonic) { + const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], + (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; + const auto minmax = std::minmax(corners); + const auto diff = + ((minmax.second - minmax.first) * rel_fac_monotonic) + abs_fac_monotonic; + f_interp[iyp] = std::max(f_interp[iyp], minmax.first - diff); + f_interp[iyp] = std::min(f_interp[iyp], minmax.second + diff); + } + ASSERT2(std::isfinite(f_interp[iyp]) || i.x() < localmesh->xstart + || i.x() > localmesh->xend); + } + } + f_interp.setRegion(region2); + ASSERT2(f_interp.getRegionID()); return f_interp; } -Field3D XZHermiteSpline::interpolate(const Field3D& f, const Field3D& delta_x, - const Field3D& delta_z, const std::string& region) { +template +Field3D XZHermiteSplineBase::interpolate(const Field3D& f, + const Field3D& delta_x, + const Field3D& delta_z, + const std::string& region) { calcWeights(delta_x, delta_z, region); return interpolate(f, region); } -Field3D XZHermiteSpline::interpolate(const Field3D& f, const Field3D& delta_x, - const Field3D& delta_z, const BoutMask& mask, - const std::string& region) { +template +Field3D XZHermiteSplineBase::interpolate(const Field3D& f, + const Field3D& delta_x, + const Field3D& delta_z, + const BoutMask& mask, + const std::string& region) { calcWeights(delta_x, delta_z, mask, region); return interpolate(f, region); } + +// ensure they are instantiated +template class XZHermiteSplineBase; +template class XZHermiteSplineBase; +template class XZHermiteSplineBase; +template class XZHermiteSplineBase; +#if BOUT_HAS_PETSC +template class XZHermiteSplineBase; +template class XZHermiteSplineBase; +#endif diff --git a/src/mesh/interpolation/interpolation_z.cxx b/src/mesh/interpolation/interpolation_z.cxx index 8d39e6baa8..e743ec8555 100644 --- a/src/mesh/interpolation/interpolation_z.cxx +++ b/src/mesh/interpolation/interpolation_z.cxx @@ -20,8 +20,10 @@ * **************************************************************************/ +#include #include #include +#include ZInterpolation::ZInterpolation(int y_offset, Mesh* mesh, Region region_in) : localmesh(mesh == nullptr ? bout::globals::mesh : mesh), region(region_in), diff --git a/src/mesh/interpolation/lagrange_4pt_xz.cxx b/src/mesh/interpolation/lagrange_4pt_xz.cxx index 92c14ecfd5..c82c9fa36e 100644 --- a/src/mesh/interpolation/lagrange_4pt_xz.cxx +++ b/src/mesh/interpolation/lagrange_4pt_xz.cxx @@ -20,6 +20,7 @@ * **************************************************************************/ +#include "fmt/format.h" #include "bout/globals.hxx" #include "bout/interpolation_xz.hxx" #include "bout/mesh.hxx" @@ -29,6 +30,11 @@ XZLagrange4pt::XZLagrange4pt(int y_offset, Mesh* mesh) : XZInterpolation(y_offset, mesh), t_x(localmesh), t_z(localmesh) { + if (localmesh->getNXPE() > 1) { + throw BoutException( + "XZLagrange4pt interpolation does not support MPI splitting in X"); + } + // Index arrays contain guard cells in order to get subscripts right i_corner.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); k_corner.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); @@ -128,7 +134,10 @@ Field3D XZLagrange4pt::interpolate(const Field3D& f, const std::string& region) // Then in X f_interp(x, y_next, z) = lagrange_4pt(xvals, t_x(x, y, z)); + ASSERT2(std::isfinite(f_interp(x, y_next, z))); } + const auto region2 = y_offset != 0 ? fmt::format("RGN_YPAR_{:+d}", y_offset) : region; + f_interp.setRegion(region2); return f_interp; } diff --git a/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx b/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx deleted file mode 100644 index abedb27733..0000000000 --- a/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx +++ /dev/null @@ -1,134 +0,0 @@ -/************************************************************************** - * Copyright 2018 B.D.Dudson, P. Hill - * - * Contact: Ben Dudson, bd512@york.ac.uk - * - * This file is part of BOUT++. - * - * BOUT++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * BOUT++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with BOUT++. If not, see . - * - **************************************************************************/ - -#include "bout/globals.hxx" -#include "bout/index_derivs_interface.hxx" -#include "bout/interpolation_xz.hxx" -#include "bout/mesh.hxx" - -#include - -Field3D XZMonotonicHermiteSpline::interpolate(const Field3D& f, - const std::string& region) const { - ASSERT1(f.getMesh() == localmesh); - Field3D f_interp(f.getMesh()); - f_interp.allocate(); - - // Derivatives are used for tension and need to be on dimensionless - // coordinates - Field3D fx = bout::derivatives::index::DDX(f, CELL_DEFAULT, "DEFAULT"); - localmesh->communicateXZ(fx); - // communicate in y, but do not calculate parallel slices - { - auto h = localmesh->sendY(fx); - localmesh->wait(h); - } - Field3D fz = bout::derivatives::index::DDZ(f, CELL_DEFAULT, "DEFAULT", "RGN_ALL"); - localmesh->communicateXZ(fz); - // communicate in y, but do not calculate parallel slices - { - auto h = localmesh->sendY(fz); - localmesh->wait(h); - } - Field3D fxz = bout::derivatives::index::DDX(fz, CELL_DEFAULT, "DEFAULT"); - localmesh->communicateXZ(fxz); - // communicate in y, but do not calculate parallel slices - { - auto h = localmesh->sendY(fxz); - localmesh->wait(h); - } - - const auto curregion{getRegion(region)}; - BOUT_FOR(i, curregion) { - const int x = i.x(); - const int y = i.y(); - const int z = i.z(); - - // Due to lack of guard cells in z-direction, we need to ensure z-index - // wraps around - const int ncz = localmesh->LocalNz; - const int z_mod = ((k_corner(x, y, z) % ncz) + ncz) % ncz; - const int z_mod_p1 = (z_mod + 1) % ncz; - - const int y_next = y + y_offset; - - // Interpolate f in X at Z - const BoutReal f_z = f(i_corner(x, y, z), y_next, z_mod) * h00_x(x, y, z) - + f(i_corner(x, y, z) + 1, y_next, z_mod) * h01_x(x, y, z) - + fx(i_corner(x, y, z), y_next, z_mod) * h10_x(x, y, z) - + fx(i_corner(x, y, z) + 1, y_next, z_mod) * h11_x(x, y, z); - - // Interpolate f in X at Z+1 - const BoutReal f_zp1 = f(i_corner(x, y, z), y_next, z_mod_p1) * h00_x(x, y, z) - + f(i_corner(x, y, z) + 1, y_next, z_mod_p1) * h01_x(x, y, z) - + fx(i_corner(x, y, z), y_next, z_mod_p1) * h10_x(x, y, z) - + fx(i_corner(x, y, z) + 1, y_next, z_mod_p1) * h11_x(x, y, z); - - // Interpolate fz in X at Z - const BoutReal fz_z = fz(i_corner(x, y, z), y_next, z_mod) * h00_x(x, y, z) - + fz(i_corner(x, y, z) + 1, y_next, z_mod) * h01_x(x, y, z) - + fxz(i_corner(x, y, z), y_next, z_mod) * h10_x(x, y, z) - + fxz(i_corner(x, y, z) + 1, y_next, z_mod) * h11_x(x, y, z); - - // Interpolate fz in X at Z+1 - const BoutReal fz_zp1 = - fz(i_corner(x, y, z), y_next, z_mod_p1) * h00_x(x, y, z) - + fz(i_corner(x, y, z) + 1, y_next, z_mod_p1) * h01_x(x, y, z) - + fxz(i_corner(x, y, z), y_next, z_mod_p1) * h10_x(x, y, z) - + fxz(i_corner(x, y, z) + 1, y_next, z_mod_p1) * h11_x(x, y, z); - - // Interpolate in Z - BoutReal result = +f_z * h00_z(x, y, z) + f_zp1 * h01_z(x, y, z) - + fz_z * h10_z(x, y, z) + fz_zp1 * h11_z(x, y, z); - - ASSERT2(std::isfinite(result) || x < localmesh->xstart || x > localmesh->xend); - - // Monotonicity - // Force the interpolated result to be in the range of the - // neighbouring cell values. This prevents unphysical overshoots, - // but also degrades accuracy near maxima and minima. - // Perhaps should only impose near boundaries, since that is where - // problems most obviously occur. - const BoutReal localmax = BOUTMAX(f(i_corner(x, y, z), y_next, z_mod), - f(i_corner(x, y, z) + 1, y_next, z_mod), - f(i_corner(x, y, z), y_next, z_mod_p1), - f(i_corner(x, y, z) + 1, y_next, z_mod_p1)); - - const BoutReal localmin = BOUTMIN(f(i_corner(x, y, z), y_next, z_mod), - f(i_corner(x, y, z) + 1, y_next, z_mod), - f(i_corner(x, y, z), y_next, z_mod_p1), - f(i_corner(x, y, z) + 1, y_next, z_mod_p1)); - - ASSERT2(std::isfinite(localmax) || x < localmesh->xstart || x > localmesh->xend); - ASSERT2(std::isfinite(localmin) || x < localmesh->xstart || x > localmesh->xend); - - if (result > localmax) { - result = localmax; - } - if (result < localmin) { - result = localmin; - } - - f_interp(x, y_next, z) = result; - } - return f_interp; -} diff --git a/src/mesh/interpolation_xz.cxx b/src/mesh/interpolation_xz.cxx index f7f0b457f2..04d20769e4 100644 --- a/src/mesh/interpolation_xz.cxx +++ b/src/mesh/interpolation_xz.cxx @@ -23,20 +23,23 @@ * **************************************************************************/ +#include "parallel/fci_comm.hxx" +#include +#include +#include +#include #include #include -#include +#include #include #include +#include void printLocation(const Field3D& var) { output << toString(var.getLocation()); } void printLocation(const Field2D& var) { output << toString(var.getLocation()); } -const char* strLocation(CELL_LOC loc) { return toString(loc).c_str(); } - const Field3D interpolate(const Field3D& f, const Field3D& delta_x, const Field3D& delta_z) { - TRACE("Interpolating 3D field"); XZLagrange4pt interpolateMethod{f.getMesh()}; return interpolateMethod.interpolate(f, delta_x, delta_z); } @@ -47,9 +50,7 @@ const Field3D interpolate(const Field2D& f, const Field3D& delta_x, } const Field3D interpolate(const Field2D& f, const Field3D& delta_x) { - TRACE("interpolate(Field2D, Field3D)"); - - Mesh* mesh = f.getMesh(); + const Mesh* mesh = f.getMesh(); ASSERT1(mesh == delta_x.getMesh()); Field3D result{emptyFrom(delta_x)}; @@ -90,6 +91,14 @@ namespace { RegisterXZInterpolation registerinterphermitespline{"hermitespline"}; RegisterXZInterpolation registerinterpmonotonichermitespline{ "monotonichermitespline"}; +RegisterXZInterpolation registerinterphermitesplines{ + "hermitesplineserial"}; +RegisterXZInterpolation + registerinterpmonotonichermitesplines{"monotonichermitesplineserial"}; +RegisterXZInterpolation registerinterphermitesplinel{ + "hermitesplinelegacy"}; +RegisterXZInterpolation + registerinterpmonotonichermitesplinel{"monotonichermitesplinelegacy"}; RegisterXZInterpolation registerinterplagrange4pt{"lagrange4pt"}; RegisterXZInterpolation registerinterpbilinear{"bilinear"}; } // namespace diff --git a/src/mesh/invert3x3.hxx b/src/mesh/invert3x3.hxx index c011f55bf7..746765d913 100644 --- a/src/mesh/invert3x3.hxx +++ b/src/mesh/invert3x3.hxx @@ -37,7 +37,6 @@ /// return. Otherwise, an empty `std::optional` is return namespace bout { inline std::optional invert3x3(Matrix& a) { - TRACE("invert3x3"); // Calculate the first co-factors const BoutReal A = a(1, 1) * a(2, 2) - a(1, 2) * a(2, 1); diff --git a/src/mesh/mesh.cxx b/src/mesh/mesh.cxx index cb6cb8410c..6d9a987cde 100644 --- a/src/mesh/mesh.cxx +++ b/src/mesh/mesh.cxx @@ -1,14 +1,38 @@ +#include +#include #include +#include +#include #include #include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include #include +#include +#include +#include +#include #include - -#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include #include "impls/bout/boutmesh.hxx" @@ -298,7 +322,6 @@ bool Mesh::sourceHasYBoundaryGuards() { return source->hasYBoundaryGuards(); } **************************************************************************/ void Mesh::communicateXZ(FieldGroup& g) { - TRACE("Mesh::communicate(FieldGroup&)"); // Send data comm_handle h = sendX(g); @@ -308,7 +331,6 @@ void Mesh::communicateXZ(FieldGroup& g) { } void Mesh::communicateYZ(FieldGroup& g) { - TRACE("Mesh::communicate(FieldGroup&)"); // Send data comm_handle h = sendY(g); @@ -325,7 +347,6 @@ void Mesh::communicateYZ(FieldGroup& g) { } void Mesh::communicate(FieldGroup& g) { - TRACE("Mesh::communicate(FieldGroup&)"); if (include_corner_cells) { // Send data in y-direction @@ -355,38 +376,28 @@ void Mesh::communicate(FieldGroup& g) { } } -/// This is a bit of a hack for now to get FieldPerp communications -/// The FieldData class needs to be changed to accomodate FieldPerp objects -void Mesh::communicate(FieldPerp& f) { - comm_handle recv[2]; - - int nin = xstart; // Number of x points in inner guard cell - int nout = LocalNx - xend - 1; // Number of x points in outer guard cell - - // Post receives for guard cell regions - - recv[0] = irecvXIn(f[0], nin * LocalNz, 0); - recv[1] = irecvXOut(f[xend + 1], nout * LocalNz, 1); - - // Send data - sendXIn(f[xstart], nin * LocalNz, 1); - sendXOut(f[xend - nout + 1], nout * LocalNz, 0); +int Mesh::msg_len(const std::vector& var_list, int xge, int xlt, int yge, + int ylt) const { + int len = 0; - // Wait for receive - wait(recv[0]); - wait(recv[1]); -} + using enum Field::FieldType; -int Mesh::msg_len(const std::vector& var_list, int xge, int xlt, int yge, - int ylt) { - int len = 0; + const auto x_length = xlt - xge; + const auto y_length = ylt - yge; + const auto z_length = LocalNz; /// Loop over variables for (const auto& var : var_list) { - if (var->is3D()) { - len += (xlt - xge) * (ylt - yge) * LocalNz * var->elementSize(); - } else { - len += (xlt - xge) * (ylt - yge) * var->elementSize(); + switch (var->field_type()) { + case field3d: + len += x_length * y_length * z_length * var->elementSize(); + break; + case field2d: + len += x_length * y_length * var->elementSize(); + break; + case fieldperp: + len += x_length * z_length * var->elementSize(); + break; } } @@ -408,34 +419,6 @@ int Mesh::ySize(int jx) const { return all; } -bool Mesh::hasBndryLowerY() { - static bool calc = false, answer; - if (calc) { - return answer; // Already calculated - } - - int mybndry = static_cast(!(iterateBndryLowerY().isDone())); - int allbndry; - mpi->MPI_Allreduce(&mybndry, &allbndry, 1, MPI_INT, MPI_BOR, getXcomm(yend)); - answer = static_cast(allbndry); - calc = true; - return answer; -} - -bool Mesh::hasBndryUpperY() { - static bool calc = false, answer; - if (calc) { - return answer; // Already calculated - } - - int mybndry = static_cast(!(iterateBndryUpperY().isDone())); - int allbndry; - mpi->MPI_Allreduce(&mybndry, &allbndry, 1, MPI_INT, MPI_BOR, getXcomm(ystart)); - answer = static_cast(allbndry); - calc = true; - return answer; -} - int Mesh::localSize3D() { if (localNumCells3D < 0) { const int xs = firstX() ? xstart - 1 : xstart; @@ -523,7 +506,7 @@ int Mesh::globalStartIndex2D() { int Mesh::globalStartIndexPerp() { int localSize = localSizePerp(); int cumulativeSize = 0; - mpi->MPI_Scan(&localSize, &cumulativeSize, 1, MPI_INT, MPI_SUM, getXcomm()); + mpi->MPI_Scan(&localSize, &cumulativeSize, 1, MPI_INT, MPI_SUM, getXZcomm()); return cumulativeSize - localSize; } @@ -540,11 +523,11 @@ const std::vector Mesh::readInts(const std::string& name, int n) { if (source->hasVar(name)) { if (!source->get(this, result, name, n, 0)) { // Error reading - throw BoutException(_("Could not read integer array '{:s}'\n"), name.c_str()); + throw BoutException(_f("Could not read integer array '{:s}'\n"), name.c_str()); } } else { // Not found - throw BoutException(_("Missing integer array {:s}\n"), name.c_str()); + throw BoutException(_f("Missing integer array {:s}\n"), name.c_str()); } return result; @@ -568,7 +551,7 @@ Mesh::createDefaultCoordinates(const CELL_LOC location, const Region<>& Mesh::getRegion3D(const std::string& region_name) const { const auto found = regionMap3D.find(region_name); if (found == end(regionMap3D)) { - throw BoutException(_("Couldn't find region {:s} in regionMap3D"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMap3D"), region_name); } return region3D[found->second]; } @@ -576,7 +559,7 @@ const Region<>& Mesh::getRegion3D(const std::string& region_name) const { size_t Mesh::getRegionID(const std::string& region_name) const { const auto found = regionMap3D.find(region_name); if (found == end(regionMap3D)) { - throw BoutException(_("Couldn't find region {:s} in regionMap3D"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMap3D"), region_name); } return found->second; } @@ -584,7 +567,7 @@ size_t Mesh::getRegionID(const std::string& region_name) const { const Region& Mesh::getRegion2D(const std::string& region_name) const { const auto found = regionMap2D.find(region_name); if (found == end(regionMap2D)) { - throw BoutException(_("Couldn't find region {:s} in regionMap2D"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMap2D"), region_name); } return found->second; } @@ -592,7 +575,7 @@ const Region& Mesh::getRegion2D(const std::string& region_name) const { const Region& Mesh::getRegionPerp(const std::string& region_name) const { const auto found = regionMapPerp.find(region_name); if (found == end(regionMapPerp)) { - throw BoutException(_("Couldn't find region {:s} in regionMapPerp"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMapPerp"), region_name); } return found->second; } @@ -611,8 +594,8 @@ bool Mesh::hasRegionPerp(const std::string& region_name) const { void Mesh::addRegion3D(const std::string& region_name, const Region<>& region) { if (regionMap3D.count(region_name)) { - throw BoutException(_("Trying to add an already existing region {:s} to regionMap3D"), - region_name); + throw BoutException( + _f("Trying to add an already existing region {:s} to regionMap3D"), region_name); } std::optional id; @@ -629,27 +612,28 @@ void Mesh::addRegion3D(const std::string& region_name, const Region<>& region) { regionMap3D[region_name] = id.value(); - output_verbose.write(_("Registered region 3D {:s}"), region_name); + output_verbose.write(_f("Registered region 3D {:s}"), region_name); output_verbose << "\n:\t" << region.getStats() << "\n"; } void Mesh::addRegion2D(const std::string& region_name, const Region& region) { if (regionMap2D.count(region_name)) { - throw BoutException(_("Trying to add an already existing region {:s} to regionMap2D"), - region_name); + throw BoutException( + _f("Trying to add an already existing region {:s} to regionMap2D"), region_name); } regionMap2D[region_name] = region; - output_verbose.write(_("Registered region 2D {:s}"), region_name); + output_verbose.write(_f("Registered region 2D {:s}"), region_name); output_verbose << "\n:\t" << region.getStats() << "\n"; } void Mesh::addRegionPerp(const std::string& region_name, const Region& region) { if (regionMapPerp.count(region_name)) { throw BoutException( - _("Trying to add an already existing region {:s} to regionMapPerp"), region_name); + _f("Trying to add an already existing region {:s} to regionMapPerp"), + region_name); } regionMapPerp[region_name] = region; - output_verbose.write(_("Registered region Perp {:s}"), region_name); + output_verbose.write(_f("Registered region Perp {:s}"), region_name); output_verbose << "\n:\t" << region.getStats() << "\n"; } @@ -666,25 +650,37 @@ void Mesh::createDefaultRegions() { addRegion3D("RGN_NOZ", Region(0, LocalNx - 1, 0, LocalNy - 1, zstart, zend, LocalNy, LocalNz, maxregionblocksize)); addRegion3D("RGN_GUARDS", mask(getRegion3D("RGN_ALL"), getRegion3D("RGN_NOBNDRY"))); + addRegion3D("RGN_XGUARDS_IN", Region(0, xstart - 1, ystart, yend, zstart, zend, + LocalNy, LocalNz, maxregionblocksize)); + addRegion3D("RGN_XGUARDS_OUT", + Region(xend + 1, LocalNx - 1, ystart, yend, zstart, zend, LocalNy, + LocalNz, maxregionblocksize)); addRegion3D("RGN_XGUARDS", - Region(0, xstart - 1, ystart, yend, zstart, zend, LocalNy, LocalNz, - maxregionblocksize) - + Region(xend + 1, LocalNx - 1, ystart, yend, zstart, zend, - LocalNy, LocalNz, maxregionblocksize)); + getRegion3D("RGN_XGUARDS_IN") + getRegion3D("RGN_XGUARDS_OUT")); + addRegion3D("RGN_YGUARDS_IN", Region(xstart, xend, 0, ystart - 1, zstart, zend, + LocalNy, LocalNz, maxregionblocksize)); + addRegion3D("RGN_YGUARDS_OUT", + Region(xstart, xend, yend + 1, LocalNy - 1, zstart, zend, LocalNy, + LocalNz, maxregionblocksize)); addRegion3D("RGN_YGUARDS", - Region(xstart, xend, 0, ystart - 1, zstart, zend, LocalNy, LocalNz, - maxregionblocksize) - + Region(xstart, xend, yend + 1, LocalNy - 1, zstart, zend, - LocalNy, LocalNz, maxregionblocksize)); + getRegion3D("RGN_YGUARDS_IN") + getRegion3D("RGN_YGUARDS_OUT")); + addRegion3D("RGN_ZGUARDS_IN", Region(xstart, xend, ystart, yend, 0, zstart - 1, + LocalNy, LocalNz, maxregionblocksize)); + addRegion3D("RGN_ZGUARDS_OUT", + Region(xstart, xend, ystart, yend, zend + 1, LocalNz - 1, LocalNy, + LocalNz, maxregionblocksize)); addRegion3D("RGN_ZGUARDS", - Region(xstart, xend, ystart, yend, 0, zstart - 1, LocalNy, LocalNz, - maxregionblocksize) - + Region(xstart, xend, ystart, yend, zend + 1, LocalNz - 1, - LocalNy, LocalNz, maxregionblocksize)); + getRegion3D("RGN_ZGUARDS_IN") + getRegion3D("RGN_ZGUARDS_OUT")); addRegion3D("RGN_NOCORNERS", (getRegion3D("RGN_NOBNDRY") + getRegion3D("RGN_XGUARDS") + getRegion3D("RGN_YGUARDS") + getRegion3D("RGN_ZGUARDS")) .unique()); + for (int offset_ = -ystart; offset_ <= ystart; ++offset_) { + const auto region = fmt::format("RGN_YPAR_{:+d}", offset_); + addRegion3D(region, Region(xstart, xend, ystart + offset_, yend + offset_, 0, + LocalNz - 1, LocalNy, LocalNz)); + } + //2D regions addRegion2D("RGN_ALL", Region(0, LocalNx - 1, 0, LocalNy - 1, 0, 0, LocalNy, 1, maxregionblocksize)); diff --git a/src/mesh/parallel/fci.cxx b/src/mesh/parallel/fci.cxx index acd4954f97..aebfeb654c 100644 --- a/src/mesh/parallel/fci.cxx +++ b/src/mesh/parallel/fci.cxx @@ -37,82 +37,147 @@ **************************************************************************/ #include "fci.hxx" + +#include "bout/assert.hxx" +#include "bout/boundary_region_iter.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" +#include "bout/field_data.hxx" +#include "bout/mesh.hxx" +#include "bout/msg_stack.hxx" +#include "bout/options.hxx" #include "bout/parallel_boundary_op.hxx" #include "bout/parallel_boundary_region.hxx" -#include -#include -#include -#include -#include +#include "bout/paralleltransform.hxx" +#include "bout/region.hxx" + +#include +#include +#include +#include +#include +#include #include +#include + +using namespace std::string_view_literals; +using bout::boundary::BoundaryRegionFCI; + +namespace { +// Get a unique name for a field based on the sign/magnitude of the offset +std::string parallel_slice_field_name(const std::string& field, int offset) { + const std::string direction = (offset > 0) ? "forward" : "backward"; + // We only have a suffix for parallel slices beyond the first + // This is for backwards compatibility + const std::string slice_suffix = + (std::abs(offset) > 1) ? "_" + std::to_string(std::abs(offset)) : ""; + return direction + "_" + field + slice_suffix; +}; + +#if BOUT_USE_METRIC_3D +void load_parallel_metric_component(std::string name, Field3D& component, int offset) { + Mesh* mesh = component.getMesh(); + Field3D tmp{mesh}; + const auto pname = parallel_slice_field_name(name, offset); + if (mesh->get(tmp, pname, 0.0, false) != 0) { + throw BoutException("Could not read {:s} from grid file!\n" + " Fix it up with `zoidberg-update-parallel-metrics `", + pname); + } + if (!component.hasParallelSlices()) { + component.splitParallelSlices(); + component.disallowCalcParallelSlices(); + component.resetRegionParallel(true); + } + auto& pcom = component.ynext(offset); + pcom.allocate(); + BOUT_FOR(i, component.getRegion("RGN_NOBNDRY")) { pcom[i.yp(offset)] = tmp[i]; } +} + +void load_parallel_metric_components(Coordinates* coords, int offset) { +#define LOAD_PAR(var) load_parallel_metric_component(#var, coords->var, offset) + LOAD_PAR(g11); + LOAD_PAR(g22); + LOAD_PAR(g33); + LOAD_PAR(g13); + LOAD_PAR(g_11); + LOAD_PAR(g_22); + LOAD_PAR(g_33); + LOAD_PAR(g_13); + LOAD_PAR(dy); + LOAD_PAR(Bxy); + +#undef LOAD_PAR +} +#endif + +} // namespace -FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& options, - int offset_, const std::shared_ptr& inner_boundary, - const std::shared_ptr& outer_boundary, bool zperiodic) - : map_mesh(mesh), offset(offset_), - region_no_boundary(map_mesh.getRegion("RGN_NOBNDRY")), +FCIMap::FCIMap(Mesh& mesh, [[maybe_unused]] const Coordinates::FieldMetric& dy, + Options& options, int offset, + const std::shared_ptr& inner_boundary, + const std::shared_ptr& outer_boundary, bool zperiodic) + : map_mesh(&mesh), offset_(offset), + region_no_boundary(map_mesh->getRegion("RGN_NOBNDRY")), corner_boundary_mask(map_mesh) { - TRACE("Creating FCIMAP for direction {:d}", offset); + TRACE("Creating FCIMAP for direction {:d}", offset_); - if (offset == 0) { + if (offset_ == 0) { throw BoutException( "FCIMap called with offset = 0; You probably didn't mean to do that"); } auto& interpolation_options = options["xzinterpolation"]; - interp = - XZInterpolationFactory::getInstance().create(&interpolation_options, &map_mesh); - interp->setYOffset(offset); + interp = XZInterpolationFactory::getInstance().create(&interpolation_options, map_mesh); + interp->setYOffset(offset_); interp_corner = - XZInterpolationFactory::getInstance().create(&interpolation_options, &map_mesh); - interp_corner->setYOffset(offset); + XZInterpolationFactory::getInstance().create(&interpolation_options, map_mesh); + interp_corner->setYOffset(offset_); // Index-space coordinates of forward/backward points - Field3D xt_prime{&map_mesh}, zt_prime{&map_mesh}; + Field3D xt_prime{map_mesh}; + Field3D zt_prime{map_mesh}; // Real-space coordinates of grid points - Field3D R{&map_mesh}, Z{&map_mesh}; + Field3D R{map_mesh}; + Field3D Z{map_mesh}; // Real-space coordinates of forward/backward points - Field3D R_prime{&map_mesh}, Z_prime{&map_mesh}; + Field3D R_prime{map_mesh}; + Field3D Z_prime{map_mesh}; - map_mesh.get(R, "R", 0.0, false); - map_mesh.get(Z, "Z", 0.0, false); - - // Get a unique name for a field based on the sign/magnitude of the offset - const auto parallel_slice_field_name = [&](std::string field) -> std::string { - const std::string direction = (offset > 0) ? "forward" : "backward"; - // We only have a suffix for parallel slices beyond the first - // This is for backwards compatibility - const std::string slice_suffix = - (std::abs(offset) > 1) ? "_" + std::to_string(std::abs(offset)) : ""; - return direction + "_" + field + slice_suffix; - }; + map_mesh->get(R, "R", 0.0, false); + map_mesh->get(Z, "Z", 0.0, false); // If we can't read in any of these fields, things will silently not // work, so best throw - if (map_mesh.get(xt_prime, parallel_slice_field_name("xt_prime"), 0.0, false) != 0) { + if (map_mesh->get(xt_prime, parallel_slice_field_name("xt_prime", offset), 0.0, false) + != 0) { throw BoutException("Could not read {:s} from grid file!\n" " Either add it to the grid file, or reduce MYG", - parallel_slice_field_name("xt_prime")); + parallel_slice_field_name("xt_prime", offset)); } - if (map_mesh.get(zt_prime, parallel_slice_field_name("zt_prime"), 0.0, false) != 0) { + if (map_mesh->get(zt_prime, parallel_slice_field_name("zt_prime", offset), 0.0, false) + != 0) { throw BoutException("Could not read {:s} from grid file!\n" " Either add it to the grid file, or reduce MYG", - parallel_slice_field_name("zt_prime")); + parallel_slice_field_name("zt_prime", offset)); } - if (map_mesh.get(R_prime, parallel_slice_field_name("R"), 0.0, false) != 0) { + if (map_mesh->get(R_prime, parallel_slice_field_name("R", offset), 0.0, false) != 0) { throw BoutException("Could not read {:s} from grid file!\n" " Either add it to the grid file, or reduce MYG", - parallel_slice_field_name("R")); + parallel_slice_field_name("R", offset)); } - if (map_mesh.get(Z_prime, parallel_slice_field_name("Z"), 0.0, false) != 0) { + if (map_mesh->get(Z_prime, parallel_slice_field_name("Z", offset), 0.0, false) != 0) { throw BoutException("Could not read {:s} from grid file!\n" " Either add it to the grid file, or reduce MYG", - parallel_slice_field_name("Z")); + parallel_slice_field_name("Z", offset)); } // Cell corners @@ -157,23 +222,28 @@ FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& interp->calcWeights(xt_prime, zt_prime); } - const int ncz = map_mesh.LocalNz; + const int ncz = map_mesh->LocalNz; BoutMask to_remove(map_mesh); - // Serial loop because call to BoundaryRegionPar::addPoint + const int xend = map_mesh->xstart + + ((map_mesh->xend - map_mesh->xstart + 1) * map_mesh->getNXPE()) - 1; + // Default to the maximum number of points + const int defValid{map_mesh->ystart - 1 + std::abs(offset)}; + // Serial loop because call to BoundaryRegionFCI::addPoint // (probably?) can't be done in parallel BOUT_FOR_SERIAL(i, xt_prime.getRegion("RGN_NOBNDRY")) { // z is periodic, so make sure the z-index wraps around if (zperiodic) { - zt_prime[i] = zt_prime[i] - - ncz * (static_cast(zt_prime[i] / static_cast(ncz))); + zt_prime[i] = + zt_prime[i] + - (ncz * (static_cast(zt_prime[i] / static_cast(ncz)))); if (zt_prime[i] < 0.0) { zt_prime[i] += ncz; } } - if ((xt_prime[i] >= map_mesh.xstart) and (xt_prime[i] <= map_mesh.xend)) { + if ((xt_prime[i] >= map_mesh->xstart) and (xt_prime[i] <= xend)) { // Not a boundary continue; } @@ -213,7 +283,7 @@ FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& const BoutReal dR_dz = 0.5 * (R[i_zp] - R[i_zm]); const BoutReal dZ_dz = 0.5 * (Z[i_zp] - Z[i_zm]); - const BoutReal det = dR_dx * dZ_dz - dR_dz * dZ_dx; // Determinant of 2x2 matrix + const BoutReal det = (dR_dx * dZ_dz) - (dR_dz * dZ_dx); // Determinant of 2x2 matrix const BoutReal dR = R_prime[i] - R[i]; const BoutReal dZ = Z_prime[i] - Z[i]; @@ -226,33 +296,24 @@ FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& // outer boundary. However, if any of the surrounding points are negative, // that also means inner. So to differentiate between inner and outer we // need at least 2 points in the domain. - ASSERT2(map_mesh.xend - map_mesh.xstart >= 2); - auto boundary = (xt_prime[i] < map_mesh.xstart) ? inner_boundary : outer_boundary; - boundary->add_point(x, y, z, x + dx, y + 0.5 * offset, - z + dz, // Intersection point in local index space - 0.5, // Distance to intersection - 1 // Default to that there is a point in the other direction - ); + ASSERT2(map_mesh->xend - map_mesh->xstart >= 2); + auto boundary = (xt_prime[i] < map_mesh->xstart) ? inner_boundary : outer_boundary; + if (!boundary->contains(x, y, z)) { + boundary->add_point(x, y, z, x + dx, y + offset - (std::copysign(0.5, offset)), + z + dz, // Intersection point in local index space + std::abs(offset) - 0.5, // Distance to intersection + defValid, offset); + } } region_no_boundary = region_no_boundary.mask(to_remove); interp->setRegion(region_no_boundary); - - const auto region = fmt::format("RGN_YPAR_{:+d}", offset); - if (not map_mesh.hasRegion3D(region)) { - // The valid region for this slice - map_mesh.addRegion3D( - region, Region(map_mesh.xstart, map_mesh.xend, map_mesh.ystart + offset, - map_mesh.yend + offset, 0, map_mesh.LocalNz - 1, - map_mesh.LocalNy, map_mesh.LocalNz)); - } } Field3D FCIMap::integrate(Field3D& f) const { - TRACE("FCIMap::integrate"); ASSERT1(f.getDirectionY() == YDirectionType::Standard); - ASSERT1(&map_mesh == f.getMesh()); + ASSERT1(map_mesh == f.getMesh()); // Cell centre values Field3D centre = interp->interpolate(f); @@ -267,7 +328,7 @@ Field3D FCIMap::integrate(Field3D& f) const { #endif BOUT_FOR(i, region_no_boundary) { - const auto inext = i.yp(offset); + const auto inext = i.yp(offset_); const BoutReal f_c = centre[inext]; const auto izm = i.zm(); const int x = i.x(); @@ -276,7 +337,7 @@ Field3D FCIMap::integrate(Field3D& f) const { const int zm = izm.z(); if (corner_boundary_mask(x, y, z) || corner_boundary_mask(x - 1, y, z) || corner_boundary_mask(x, y, zm) || corner_boundary_mask(x - 1, y, zm) - || (x == map_mesh.xstart)) { + || (x == map_mesh->xstart)) { // One of the corners leaves the domain. // Use the cell centre value, since boundary conditions are not // currently applied to corners. @@ -297,24 +358,79 @@ Field3D FCIMap::integrate(Field3D& f) const { return result; } +FCITransform::FCITransform(Mesh& mesh, const Coordinates::FieldMetric& dy, bool zperiodic, + Options* opt) + : ParallelTransform(mesh, opt), R{&mesh}, Z{&mesh} { + + // check the coordinate system used for the grid data source + FCITransform::checkInputGrid(); + + // Real-space coordinates of grid cells + mesh.get(R, "R", 0.0, false); + mesh.get(Z, "Z", 0.0, false); + + auto forward_boundary_xin = + std::make_shared("FCI_forward", BNDRY_PAR_FWD_XIN, +1, &mesh); + auto backward_boundary_xin = + std::make_shared("FCI_backward", BNDRY_PAR_BKWD_XIN, -1, &mesh); + auto forward_boundary_xout = + std::make_shared("FCI_forward", BNDRY_PAR_FWD_XOUT, +1, &mesh); + auto backward_boundary_xout = + std::make_shared("FCI_backward", BNDRY_PAR_BKWD_XOUT, -1, &mesh); + + // Add the boundary region to the mesh's vector of parallel boundaries + mesh.addBoundaryPar(forward_boundary_xin, BoundaryParType::xin_fwd); + mesh.addBoundaryPar(backward_boundary_xin, BoundaryParType::xin_bwd); + mesh.addBoundaryPar(forward_boundary_xout, BoundaryParType::xout_fwd); + mesh.addBoundaryPar(backward_boundary_xout, BoundaryParType::xout_bwd); + + field_line_maps.reserve(static_cast(mesh.ystart) * 2); + for (int offset = 1; offset < mesh.ystart + 1; ++offset) { + field_line_maps.emplace_back(mesh, dy, options, offset, forward_boundary_xin, + forward_boundary_xout, zperiodic); + field_line_maps.emplace_back(mesh, dy, options, -offset, backward_boundary_xin, + backward_boundary_xout, zperiodic); + } + const std::array bndries = {forward_boundary_xin, forward_boundary_xout, + backward_boundary_xin, backward_boundary_xout}; + for (const auto& bndry : bndries) { + for (const auto& bndry2 : bndries) { + if (bndry->dir() == bndry2->dir()) { + continue; + } + for (auto point : *bndry) { + for (auto point2 : *bndry2) { + if (point.ind() == point2.ind()) { + // This point has a boundary in both directions. Calculate the + // distance between two points, to check how many non-boundary + // points exist. + point.setValid(static_cast( + std::abs((point2.offset() - point.offset())) - 2)); + } + } + } + } + } +} + void FCITransform::checkInputGrid() { std::string parallel_transform; if (mesh.isDataSourceGridFile() - && !mesh.get(parallel_transform, "parallel_transform")) { + && (mesh.get(parallel_transform, "parallel_transform") == 0)) { if (parallel_transform != "fci") { throw BoutException( "Incorrect parallel transform type '" + parallel_transform + "' used " "to generate metric components for FCITransform. Should be 'fci'."); } - } // else: parallel_transform variable not found in grid input, indicates older input - // file or grid from options so must rely on the user having ensured the type is - // correct + } + // else: parallel_transform variable not found in grid input, indicates older input + // file or grid from options so must rely on the user having ensured the type is + // correct } void FCITransform::calcParallelSlices(Field3D& f) { - TRACE("FCITransform::calcParallelSlices"); - + ASSERT1(f.areCalcParallelSlicesAllowed()); ASSERT1(f.getDirectionY() == YDirectionType::Standard); // Only have forward_map/backward_map for CELL_CENTRE, so can only deal with // CELL_CENTRE inputs @@ -325,13 +441,12 @@ void FCITransform::calcParallelSlices(Field3D& f) { // Interpolate f onto yup and ydown fields for (const auto& map : field_line_maps) { - f.ynext(map.offset) = map.interpolate(f); - f.ynext(map.offset).setRegion(fmt::format("RGN_YPAR_{:+d}", map.offset)); + f.ynext(map.offset()) = map.interpolate(f); + f.ynext(map.offset()).setRegion(fmt::format("RGN_YPAR_{:+d}", map.offset())); } } void FCITransform::integrateParallelSlices(Field3D& f) { - TRACE("FCITransform::integrateParallelSlices"); ASSERT1(f.getDirectionY() == YDirectionType::Standard); // Only have forward_map/backward_map for CELL_CENTRE, so can only deal with @@ -343,6 +458,33 @@ void FCITransform::integrateParallelSlices(Field3D& f) { // Integrate f onto yup and ydown fields for (const auto& map : field_line_maps) { - f.ynext(map.offset) = map.integrate(f); + f.ynext(map.offset()) = map.integrate(f); } } + +void FCITransform::outputVars(Options& output_options) { + // Real-space coordinates of grid points + output_options["R"].force(R, "FCI"); + output_options["Z"].force(Z, "FCI"); +} + +void FCITransform::loadParallelMetrics([[maybe_unused]] Coordinates* coords) { +#if BOUT_USE_METRIC_3D + output_info.write("\tLoading parallel metrics\n"); + const Coordinates::FieldMetric JB0 = coords->J * coords->Bxy; + coords->J.splitParallelSlices(); + coords->J.disallowCalcParallelSlices(); + coords->J.resetRegionParallel(true); + for (int i = 1; i <= mesh.ystart; ++i) { + load_parallel_metric_components(coords, -i); + load_parallel_metric_components(coords, i); + + coords->J.ynext(i).allocate(); + coords->J.ynext(-i).allocate(); + BOUT_FOR(j, JB0.getRegion("RGN_NOBNDRY")) { + coords->J.ynext(i)[j.yp(i)] = JB0[j] / coords->Bxy.ynext(i)[j.yp(i)]; + coords->J.ynext(-i)[j.yp(-i)] = JB0[j] / coords->Bxy.ynext(-i)[j.yp(-i)]; + } + } +#endif +} diff --git a/src/mesh/parallel/fci.hxx b/src/mesh/parallel/fci.hxx index 3ec3321a6a..a90e3e98dd 100644 --- a/src/mesh/parallel/fci.hxx +++ b/src/mesh/parallel/fci.hxx @@ -26,6 +26,12 @@ #ifndef BOUT_FCITRANSFORM_H #define BOUT_FCITRANSFORM_H +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" +#include "bout/coordinates.hxx" +#include "bout/region.hxx" +#include #include #include #include @@ -33,25 +39,25 @@ #include #include +#include #include +class FieldPerp; +class Field2D; +class Field3D; +class Options; + /// Field line map - contains the coefficients for interpolation class FCIMap { /// Interpolation objects std::unique_ptr interp; // Cell centre std::unique_ptr interp_corner; // Cell corner at (x+1, z+1) -public: - FCIMap() = delete; - FCIMap(Mesh& mesh, const Coordinates::FieldMetric& dy, Options& options, int offset, - const std::shared_ptr& inner_boundary, - const std::shared_ptr& outer_boundary, bool zperiodic); - // The mesh this map was created on - Mesh& map_mesh; + Mesh* map_mesh; /// Direction of map - const int offset; + int offset_; /// region containing all points where the field line has not left the /// domain @@ -59,8 +65,18 @@ public: /// If any of the integration area has left the domain BoutMask corner_boundary_mask; +public: + FCIMap() = delete; + FCIMap(Mesh& mesh, const Coordinates::FieldMetric& dy, Options& options, int offset, + const std::shared_ptr& inner_boundary, + const std::shared_ptr& outer_boundary, + bool zperiodic); + + /// Direction of map + int offset() const { return offset_; } + Field3D interpolate(Field3D& f) const { - ASSERT1(&map_mesh == f.getMesh()); + ASSERT1(map_mesh == f.getMesh()); return interp->interpolate(f); } @@ -72,51 +88,7 @@ class FCITransform : public ParallelTransform { public: FCITransform() = delete; FCITransform(Mesh& mesh, const Coordinates::FieldMetric& dy, bool zperiodic = true, - Options* opt = nullptr) - : ParallelTransform(mesh, opt) { - - // check the coordinate system used for the grid data source - FCITransform::checkInputGrid(); - - auto forward_boundary_xin = - std::make_shared("FCI_forward", BNDRY_PAR_FWD_XIN, +1, &mesh); - auto backward_boundary_xin = std::make_shared( - "FCI_backward", BNDRY_PAR_BKWD_XIN, -1, &mesh); - auto forward_boundary_xout = - std::make_shared("FCI_forward", BNDRY_PAR_FWD_XOUT, +1, &mesh); - auto backward_boundary_xout = std::make_shared( - "FCI_backward", BNDRY_PAR_BKWD_XOUT, -1, &mesh); - - // Add the boundary region to the mesh's vector of parallel boundaries - mesh.addBoundaryPar(forward_boundary_xin, BoundaryParType::xin_fwd); - mesh.addBoundaryPar(backward_boundary_xin, BoundaryParType::xin_bwd); - mesh.addBoundaryPar(forward_boundary_xout, BoundaryParType::xout_fwd); - mesh.addBoundaryPar(backward_boundary_xout, BoundaryParType::xout_bwd); - - field_line_maps.reserve(mesh.ystart * 2); - for (int offset = 1; offset < mesh.ystart + 1; ++offset) { - field_line_maps.emplace_back(mesh, dy, options, offset, forward_boundary_xin, - forward_boundary_xout, zperiodic); - field_line_maps.emplace_back(mesh, dy, options, -offset, backward_boundary_xin, - backward_boundary_xout, zperiodic); - } - ASSERT0(mesh.ystart == 1); - std::shared_ptr bndries[]{ - forward_boundary_xin, forward_boundary_xout, backward_boundary_xin, - backward_boundary_xout}; - for (auto& bndry : bndries) { - for (const auto& bndry2 : bndries) { - if (bndry->dir == bndry2->dir) { - continue; - } - for (bndry->first(); !bndry->isDone(); bndry->next()) { - if (bndry2->contains(*bndry)) { - bndry->setValid(0); - } - } - } - } - } + Options* opt = nullptr); void calcParallelSlices(Field3D& f) override; @@ -142,6 +114,10 @@ public: bool canToFromFieldAligned() const override { return false; } + /// Save mesh variables to output + /// If R and Z(x,y,z) coordinates are in the input then these are saved to output. + void outputVars(Options& output_options) override; + bool requiresTwistShift(bool UNUSED(twist_shift_enabled), [[maybe_unused]] YDirectionType ytype) override { // No Field3Ds require twist-shift, because they cannot be field-aligned @@ -150,12 +126,17 @@ public: return false; } + void loadParallelMetrics(Coordinates* coords) override; + protected: void checkInputGrid() override; private: /// FCI maps for each of the parallel slices std::vector field_line_maps; + + /// Real-space coordinates of grid points + Field3D R, Z; }; #endif // BOUT_FCITRANSFORM_H diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx new file mode 100644 index 0000000000..3d213ad6e4 --- /dev/null +++ b/src/mesh/parallel/fci_comm.cxx @@ -0,0 +1,224 @@ +/************************************************************************** + * Communication for Flux-coordinate Independent interpolation + * + ************************************************************************** + * Copyright 2025 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov + * + * This file is part of BOUT++. + * + * BOUT++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * BOUT++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with BOUT++. If not, see . + * + **************************************************************************/ + +#include "fci_comm.hxx" +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/field3d.hxx" +#include "bout/region.hxx" + +#include +#include +#include +#include + +fci_comm::ProcLocal fci_comm::GlobalToLocal1D::convert(int id) const { + if (periodic) { + while (id < mg) { + id += global; + } + while (id >= global + mg) { + id -= global; + } + } + const int idwo = id - mg; + int proc = idwo / local; + if (not periodic) { + if (proc >= npe) { + proc = npe - 1; + } + } + const int loc = id - (local * proc); +#if CHECK > 1 + ASSERT1(loc >= 0); + ASSERT1(loc <= localwith); + ASSERT1(proc >= 0); + ASSERT1(proc < npe); + if (periodic and (loc < mg or loc >= local + mg)) { + throw BoutException( + "GlobalToLocal1D failure - expected {} < {} < {} because we are periodic\n", mg, + loc, local + mg); + } +#endif + return {proc, loc}; +} + +void GlobalField3DAccess::setup() { + // We need to send a list of data to every processor of which data + // we want We also get a list of all the data that every other + // processor wants Some of theses lists may be empty. We still need + // to send them, later we can skip that. We also compute where the + // requested data will be stored later on. This is currently + // implemented as a map, to memory efficient, as the data is + // sparse. We could also store the mapping as a dense array, if it + // turns out this lookup is not fast enough, but that may limit + // scaling at some point. + ASSERT2(is_setup == false); +#ifdef _OPENMP + for (auto& o_id : openmp_ids) { + ids.merge(o_id); + } + openmp_ids.clear(); +#endif + toGet.resize(static_cast(global2local_x.getNPE() * global2local_y.getNPE() + * global2local_z.getNPE())); + for (const auto id : ids) { + const IndG3D gind{id, global2local_y.getGlobalWith(), global2local_z.getGlobalWith()}; + const auto pix = global2local_x.convert(gind.x()); + const auto piy = global2local_y.convert(gind.y()); + const auto piz = global2local_z.convert(gind.z()); + ASSERT3(piz.proc == 0); + toGet[mesh->getProcIndex(pix.proc, piy.proc, piz.proc)].push_back( + xyzlocal.convert(pix.index, piy.index, piz.index).ind); + } + for (auto& v : toGet) { + std::sort(v.begin(), v.end()); + } + commCommLists(); + { + int offset = 0; + for (const auto& get : toGet) { + getOffsets.push_back(offset); + offset += get.size(); + } + getOffsets.push_back(offset); + } + for (const auto id : ids) { + const IndG3D gind{id, global2local_y.getGlobalWith(), global2local_z.getGlobalWith()}; + const auto pix = global2local_x.convert(gind.x()); + const auto piy = global2local_y.convert(gind.y()); + const auto piz = global2local_z.convert(gind.z()); + ASSERT3(piz.proc == 0); + const auto proc = mesh->getProcIndex(pix.proc, piy.proc, piz.proc); + const auto& vec = toGet[proc]; + const auto tofind = xyzlocal.convert(pix.index, piy.index, piz.index).ind; + auto it = std::lower_bound(vec.begin(), vec.end(), tofind); + ASSERT3(it != vec.end()); + ASSERT3(*it == tofind); + mapping[id] = std::distance(vec.begin(), it) + getOffsets[proc]; + } + is_setup = true; +} + +void GlobalField3DAccess::commCommLists() { + toSend.resize(toGet.size()); + std::vector toGetSizes(toGet.size(), -1); + std::vector toSendSizes(toSend.size(), -1); +#if CHECK > 3 + { + int thisproc; + MPI_Comm_rank(comm, &thisproc); + ASSERT0(thisproc + == mesh->getProcIndex(mesh->getXProcIndex(), mesh->getYProcIndex(), + mesh->getZProcIndex())); + } +#endif + std::vector reqs(toSend.size()); + for (size_t proc = 0; proc < toGet.size(); ++proc) { + auto ret = MPI_Irecv(&toSendSizes[proc], 1, MPI_INT, proc, 666, comm, &reqs[proc]); + ASSERT0(ret == MPI_SUCCESS); + } + for (size_t proc = 0; proc < toGet.size(); ++proc) { + toGetSizes[proc] = toGet[proc].size(); + auto ret = MPI_Send(&toGetSizes[proc], 1, MPI_INT, proc, 666, comm); + ASSERT0(ret == MPI_SUCCESS); + } + std::vector reqs2(toSend.size()); + int cnt = 0; + for ([[maybe_unused]] auto dummy : reqs) { + int ind{0}; + auto ret = MPI_Waitany(reqs.size(), reqs.data(), &ind, MPI_STATUS_IGNORE); + ASSERT0(ret == MPI_SUCCESS); + ASSERT3(ind != MPI_UNDEFINED); + ASSERT2(static_cast(ind) < toSend.size()); + ASSERT3(toSendSizes[ind] >= 0); + if (toSendSizes[ind] == 0) { + continue; + } + sendBufferSize += toSendSizes[ind]; + toSend[ind].resize(toSendSizes[ind], -1); + + ret = MPI_Irecv(toSend[ind].data(), toSend[ind].size(), MPI_INT, ind, 666 * 666, comm, + reqs2.data() + cnt++); + ASSERT0(ret == MPI_SUCCESS); + } + for (size_t proc = 0; proc < toGet.size(); ++proc) { + if (!toGet[proc].empty()) { + const auto ret = MPI_Send(toGet[proc].data(), toGet[proc].size(), MPI_INT, proc, + 666 * 666, comm); + ASSERT0(ret == MPI_SUCCESS); + } + } + for (int c = 0; c < cnt; c++) { + int ind{0}; + const auto ret = MPI_Waitany(cnt, reqs2.data(), &ind, MPI_STATUS_IGNORE); + ASSERT0(ret == MPI_SUCCESS); + ASSERT3(ind != MPI_UNDEFINED); + } +} + +std::vector GlobalField3DAccess::communicate_data(const Field3D& f) { + // Ensure setup is called, to setup communication pattern + if (not is_setup) { + setup(); + } + // We now send the previosly requested data to every processor, that wanted some. + // We also get the data we requested. + ASSERT2(f.getMesh() == mesh); + std::vector data(getOffsets.back()); + std::vector sendBuffer(sendBufferSize); + std::vector reqs(toSend.size()); + int cnt1 = 0; + for (size_t proc = 0; proc < toGet.size(); ++proc) { + if (toGet[proc].empty()) { + continue; + } + auto ret = MPI_Irecv(data.data() + getOffsets[proc], toGet[proc].size(), MPI_DOUBLE, + proc, 666, comm, reqs.data() + cnt1); + ASSERT0(ret == MPI_SUCCESS); + cnt1++; + } + int cnt = 0; + for (size_t proc = 0; proc < toGet.size(); ++proc) { + if (toSend[proc].empty()) { + continue; + } + const void* start = sendBuffer.data() + cnt; + for (auto i : toSend[proc]) { + sendBuffer[cnt++] = f[Ind3D(i)]; + } + auto ret = MPI_Send(start, toSend[proc].size(), MPI_DOUBLE, proc, 666, comm); + ASSERT0(ret == MPI_SUCCESS); + } + for (int j = 0; j < cnt1; ++j) { + int ind{0}; + auto ret = MPI_Waitany(cnt1, reqs.data(), &ind, MPI_STATUS_IGNORE); + ASSERT0(ret == MPI_SUCCESS); + ASSERT3(ind != MPI_UNDEFINED); + ASSERT3(ind >= 0); + ASSERT3(ind < cnt1); + } + return data; +} diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx new file mode 100644 index 0000000000..324cae8a22 --- /dev/null +++ b/src/mesh/parallel/fci_comm.hxx @@ -0,0 +1,187 @@ +/************************************************************************** + * Communication for Flux-coordinate Independent interpolation + * + ************************************************************************** + * Copyright 2025 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov + * + * This file is part of BOUT++. + * + * BOUT++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * BOUT++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with BOUT++. If not, see . + * + **************************************************************************/ + +#pragma once + +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutcomm.hxx" +#include "bout/field3d.hxx" +#include "bout/mesh.hxx" +#include "bout/region.hxx" +#include +#include +#include +#include +#include +#include + +/// GlobalField3DAccess is a class to set up the communication +/// patterns, to request abitrary data form the global +/// field. GlobalField3DAccessInstance is an instance. after +/// GlobalField3DAccess has communicated the pre-requested data. Only +/// data that has been pre-requested can be requested from +/// GlobalField3DAccessInstance after communication. +/// +/// The usage looks a bit like this: +/// +/// GlobalField3DAccess gfa; +/// // Request an abitrary number of global indices ``gi``: +/// gfa.request(gi) +/// // Communicate data +/// const auto data = gfa.communicate(f3d); +/// // Now data can be accesssed for all previously requested ``gi``s: +/// data[gi] +class GlobalField3DAccess; + +namespace fci_comm { +struct ProcLocal { + int proc; + int index; +}; + +/// Class to convert global to local indices for 1D +/// given the global index, it returns the local index and the processor. +struct GlobalToLocal1D { + GlobalToLocal1D(int mg, int npe, int localwith, bool periodic) + : mg(mg), npe(npe), localwith(localwith), local(localwith - (2 * mg)), + global(local * npe), globalwith(global + (2 * mg)), periodic(periodic){}; + ProcLocal convert(int id) const; + int getLocalWith() const { return localwith; } + int getGlobalWith() const { return globalwith; } + int getNPE() const { return npe; } + +private: + int mg; + int npe; + int localwith; + int local; + int global; + int globalwith; + bool periodic; +}; + +/// Convert an x-y-z tupple to an Ind +template +struct XYZ2Ind { + XYZ2Ind(const int nx, const int ny, const int nz) : nx(nx), ny(ny), nz(nz) {} + ind convert(const int x, const int y, const int z) const { + return {z + ((y + x * ny) * nz), ny, nz}; + } + ind operator()(const int x, const int y, const int z) const { return convert(x, y, z); } + +private: + int nx; + int ny; + int nz; +}; +} // namespace fci_comm + +class GlobalField3DAccessInstance { +public: + const BoutReal& operator[](IndG3D ind) const; + GlobalField3DAccessInstance(const GlobalField3DAccess* gfa, + std::vector&& data) + : gfa(gfa), data(std::move(data)){}; + +private: + const GlobalField3DAccess* gfa; + std::vector data; +}; + +class GlobalField3DAccess { +public: + friend class GlobalField3DAccessInstance; + GlobalField3DAccess(Mesh* mesh) + : mesh(mesh), + global2local_x(mesh->xstart, mesh->getNXPE(), mesh->LocalNx, mesh->periodicX), + global2local_y(mesh->ystart, mesh->getNYPE(), mesh->LocalNy, true), + global2local_z(mesh->zstart, mesh->getNZPE(), mesh->LocalNz, true), + xyzlocal(global2local_x.getLocalWith(), global2local_y.getLocalWith(), + global2local_z.getLocalWith()), + xyzglobal(global2local_x.getGlobalWith(), global2local_y.getGlobalWith(), + global2local_z.getGlobalWith()), + comm(BoutComm::get()) { +#ifdef _OPENMP + openmp_ids.resize(omp_get_max_threads()); +#endif +#if CHECK >= 2 + // We could also allow false, but then we would need to ensure it + // is false everywhere. + for (int x = 0; x < mesh->LocalNx; ++x) { + ASSERT2(mesh->periodicY(x) == true); + } +#endif + }; + void request(IndG3D ind) { + ASSERT2(is_setup == false); +#ifdef _OPENMP + ASSERT2(openmp_ids.size() > static_cast(omp_get_thread_num())); + openmp_ids[omp_get_thread_num()].emplace(ind.ind); +#else + ids.emplace(ind.ind); +#endif + } + + GlobalField3DAccessInstance communicate(const Field3D& f) { + return {this, communicate_data(f)}; + } + std::unique_ptr communicate_asPtr(const Field3D& f) { + return std::make_unique(this, communicate_data(f)); + } + +private: + void setup(); + void commCommLists(); + Mesh* mesh; +#ifdef _OPENMP + // openmp thread-local variable + std::vector> openmp_ids; +#endif + std::set ids; + std::map mapping; + bool is_setup{false}; + fci_comm::GlobalToLocal1D global2local_x; + fci_comm::GlobalToLocal1D global2local_y; + fci_comm::GlobalToLocal1D global2local_z; + +public: + fci_comm::XYZ2Ind xyzlocal; + fci_comm::XYZ2Ind xyzglobal; + +private: + std::vector> toGet; + std::vector> toSend; + std::vector getOffsets; + int sendBufferSize{0}; + MPI_Comm comm; + std::vector communicate_data(const Field3D& f); +}; + +inline const BoutReal& GlobalField3DAccessInstance::operator[](IndG3D ind) const { + auto it = gfa->mapping.find(ind.ind); + ASSERT2(it != gfa->mapping.end()); + return data[it->second]; +} diff --git a/src/mesh/parallel/shiftedmetric.cxx b/src/mesh/parallel/shiftedmetric.cxx index 382052047d..705c48e944 100644 --- a/src/mesh/parallel/shiftedmetric.cxx +++ b/src/mesh/parallel/shiftedmetric.cxx @@ -17,6 +17,11 @@ #include +#if BOUT_HAS_CUDA +#include +#include +#endif + ShiftedMetric::ShiftedMetric(Mesh& m, CELL_LOC location_in, Field2D zShift_, BoutReal zlength_in, Options* opt) : ParallelTransform(m, opt), location(location_in), zShift(std::move(zShift_)), @@ -24,6 +29,7 @@ ShiftedMetric::ShiftedMetric(Mesh& m, CELL_LOC location_in, Field2D zShift_, ASSERT1(zShift.getLocation() == location); // check the coordinate system used for the grid data source ShiftedMetric::checkInputGrid(); + bout::fft::assertZSerial(m, "ShiftedMetric"); cachePhases(); } @@ -38,8 +44,8 @@ void ShiftedMetric::checkInputGrid() { "Should be 'shiftedmetric'."); } } // else: parallel_transform variable not found in grid input, indicates older input - // file or grid from options so must rely on the user having ensured the type is - // correct + // file or grid from options so must rely on the user having ensured the type is + // correct } void ShiftedMetric::outputVars(Options& output_options) { @@ -222,6 +228,260 @@ void ShiftedMetric::shiftZ(const BoutReal* in, const dcomplex* phs, BoutReal* ou irfft(&cmplx[0], mesh.LocalNz, out); // Reverse FFT } +#if BOUT_HAS_CUDA +// Bit-reversal +__device__ inline unsigned int bit_reverse(unsigned int x, unsigned int log2n) { + unsigned int result = 0; +#pragma unroll + for (unsigned int i = 0; i < log2n; i++) { + result = (result << 1) | (x & 1); + x >>= 1; + } + return result; +} + +// Block-level cooperative FFT +// Multiple threads cooperate on each FFT using shared memory +template +__global__ void fft_block_cooperative(const BoutReal** __restrict__ in, + BoutReal** __restrict__ out, + const double2** __restrict__ blocks_phs, + const int nbatches, const int nblocks) { + + constexpr int LOG2_NZ = __builtin_ctz(NZ); + constexpr double INV_NZ = 1.0 / (double)NZ; + constexpr int NMODES = (NZ / 2) + 1; + + // Shared memory for FFTS_PER_BLOCK FFTs + // Each FFT needs NZ complex values + __shared__ double2 shared_fft[FFTS_PER_BLOCK][NZ]; + + // Select twiddles based on size + const double2* twiddles; + if constexpr (NZ == 16) { + twiddles = c_twiddle_16; + } else if constexpr (NZ == 64) { + twiddles = c_twiddle_64; + } else if constexpr (NZ == 128) { + twiddles = c_twiddle_128; + } else if constexpr (NZ == 256) { + twiddles = c_twiddle_256; + } else if constexpr (NZ == 512) { + twiddles = c_twiddle_512; + } else { + static_assert(NZ == 16 || NZ == 64 || NZ == 128 || NZ == 256 || NZ == 512, + "Unsupported NZ"); + } + + // Each block processes FFTS_PER_BLOCK FFTs + const int fft_id_in_block = + threadIdx.y; // Which FFT this thread works on (0 to FFTS_PER_BLOCK-1) + const int global_fft_id = blockIdx.x * FFTS_PER_BLOCK + fft_id_in_block; + + if (global_fft_id >= nblocks * nbatches) + return; + + const int block = global_fft_id / nbatches; + const int batch = global_fft_id % nbatches; + + const double* __restrict__ in_line = in[block] + batch * NZ; + double* __restrict__ out_line = out[block] + batch * NZ; + const double2* __restrict__ phs = blocks_phs[block]; + + // Thread ID within the FFT computation + const int tid = threadIdx.x; + const int threads_per_fft = blockDim.x; // All threads in x-dimension work on same FFT + + // ===== LOAD INPUT WITH BIT-REVERSAL ===== + // Each thread loads some elements (strided) + for (int i = tid; i < NZ; i += threads_per_fft) { + const unsigned int rev_i = bit_reverse(i, LOG2_NZ); + shared_fft[fft_id_in_block][rev_i].x = in_line[i]; + shared_fft[fft_id_in_block][rev_i].y = 0.0; + } + __syncthreads(); + + // ===== FORWARD FFT: Cooley-Tukey DIT in Shared Memory ===== + for (int stage = 0; stage < LOG2_NZ; ++stage) { + const int m = 1 << (stage + 1); + const int m_half = m >> 1; + + // Each thread processes multiple butterflies + for (int k = tid; k < NZ / 2; k += threads_per_fft) { + const int butterfly_group = k / m_half; + const int j = k % m_half; + const int idx_top = butterfly_group * m + j; + const int idx_bot = idx_top + m_half; + + // Twiddle factor + const int twiddle_k = (j * NZ) / m; + const double wr = twiddles[twiddle_k].x; + const double wi = twiddles[twiddle_k].y; + + // Load from shared memory + const double top_r = shared_fft[fft_id_in_block][idx_top].x; + const double top_i = shared_fft[fft_id_in_block][idx_top].y; + const double bot_r = shared_fft[fft_id_in_block][idx_bot].x; + const double bot_i = shared_fft[fft_id_in_block][idx_bot].y; + + // Butterfly: t = W * bottom + const double t_r = wr * bot_r - wi * bot_i; + const double t_i = wr * bot_i + wi * bot_r; + + // Write back + shared_fft[fft_id_in_block][idx_top].x = top_r + t_r; + shared_fft[fft_id_in_block][idx_top].y = top_i + t_i; + shared_fft[fft_id_in_block][idx_bot].x = top_r - t_r; + shared_fft[fft_id_in_block][idx_bot].y = top_i - t_i; + } + __syncthreads(); + } + + // ===== APPLY PHASE SHIFT ===== + for (int k = tid; k < NMODES; k += threads_per_fft) { + const double2 ph = phs[batch * NMODES + k]; + const double real = shared_fft[fft_id_in_block][k].x; + const double imag = shared_fft[fft_id_in_block][k].y; + shared_fft[fft_id_in_block][k].x = real * ph.x - imag * ph.y; + shared_fft[fft_id_in_block][k].y = real * ph.y + imag * ph.x; + } + + for (int k = tid + NMODES; k < NZ; k += threads_per_fft) { + const int kk = NZ - k; + const double2 tmp = phs[batch * NMODES + kk]; + const double real = shared_fft[fft_id_in_block][k].x; + const double imag = shared_fft[fft_id_in_block][k].y; + shared_fft[fft_id_in_block][k].x = real * tmp.x + imag * tmp.y; + shared_fft[fft_id_in_block][k].y = -real * tmp.y + imag * tmp.x; + } + __syncthreads(); + + // ===== INVERSE FFT: Conjugate, FFT, Conjugate ===== + // Conjugate input + for (int i = tid; i < NZ; i += threads_per_fft) { + shared_fft[fft_id_in_block][i].y = -shared_fft[fft_id_in_block][i].y; + } + __syncthreads(); + + // Bit-reverse with standard swap to avoid temp array + // This is tricky but saves memory + for (int i = tid; i < NZ / 2; i += threads_per_fft) { + const unsigned int rev_i = bit_reverse(i, LOG2_NZ); + if (i < rev_i) { // Only swap once per pair + double2 temp = shared_fft[fft_id_in_block][i]; + shared_fft[fft_id_in_block][i] = shared_fft[fft_id_in_block][rev_i]; + shared_fft[fft_id_in_block][rev_i] = temp; + } + } + __syncthreads(); + + // Forward FFT again (for inverse) + for (int stage = 0; stage < LOG2_NZ; ++stage) { + const int m = 1 << (stage + 1); + const int m_half = m >> 1; + + for (int k = tid; k < NZ / 2; k += threads_per_fft) { + const int butterfly_group = k / m_half; + const int j = k % m_half; + const int idx_top = butterfly_group * m + j; + const int idx_bot = idx_top + m_half; + + const int twiddle_k = (j * NZ) / m; + const double wr = twiddles[twiddle_k].x; + const double wi = twiddles[twiddle_k].y; + + const double top_r = shared_fft[fft_id_in_block][idx_top].x; + const double top_i = shared_fft[fft_id_in_block][idx_top].y; + const double bot_r = shared_fft[fft_id_in_block][idx_bot].x; + const double bot_i = shared_fft[fft_id_in_block][idx_bot].y; + + const double t_r = wr * bot_r - wi * bot_i; + const double t_i = wr * bot_i + wi * bot_r; + + shared_fft[fft_id_in_block][idx_top].x = top_r + t_r; + shared_fft[fft_id_in_block][idx_top].y = top_i + t_i; + shared_fft[fft_id_in_block][idx_bot].x = top_r - t_r; + shared_fft[fft_id_in_block][idx_bot].y = top_i - t_i; + } + __syncthreads(); + } + + // Store output (conjugate and normalize) + for (int i = tid; i < NZ; i += threads_per_fft) { + out_line[i] = shared_fft[fft_id_in_block][i].x * INV_NZ; + } +} + +// Launcher for block-level cooperative FFT +static void shiftZ_block_fft(const int Nz, const BoutReal** in, BoutReal** out, + const double2** phs, int nblocks, int nbatches, + cudaStream_t stream = 0) { + if ((Nz & (Nz - 1)) != 0) { + fprintf(stderr, "Error: Nz=%d must be power of 2\n", Nz); + return; + } + + const int total_ffts = nblocks * nbatches; + + if (Nz == 16) { + constexpr int FFTS_PER_BLOCK = 16; + constexpr int THREADS_PER_FFT = 16; + + dim3 block(THREADS_PER_FFT, FFTS_PER_BLOCK); + dim3 grid((total_ffts + FFTS_PER_BLOCK - 1) / FFTS_PER_BLOCK); + + fft_block_cooperative<16, FFTS_PER_BLOCK> + <<>>(in, out, phs, nbatches, nblocks); + } else if (Nz == 64) { + constexpr int FFTS_PER_BLOCK = 4; + constexpr int THREADS_PER_FFT = 64; + + dim3 block(THREADS_PER_FFT, FFTS_PER_BLOCK); + dim3 grid((total_ffts + FFTS_PER_BLOCK - 1) / FFTS_PER_BLOCK); + + fft_block_cooperative<64, FFTS_PER_BLOCK> + <<>>(in, out, phs, nbatches, nblocks); + + } else if (Nz == 128) { + constexpr int FFTS_PER_BLOCK = 2; + constexpr int THREADS_PER_FFT = 128; + + dim3 block(THREADS_PER_FFT, FFTS_PER_BLOCK); + dim3 grid((total_ffts + FFTS_PER_BLOCK - 1) / FFTS_PER_BLOCK); + + fft_block_cooperative<128, FFTS_PER_BLOCK> + <<>>(in, out, phs, nbatches, nblocks); + + } else if (Nz == 256) { + constexpr int FFTS_PER_BLOCK = 1; + constexpr int THREADS_PER_FFT = 256; + + dim3 block(THREADS_PER_FFT, FFTS_PER_BLOCK); + dim3 grid(total_ffts); + + fft_block_cooperative<256, FFTS_PER_BLOCK> + <<>>(in, out, phs, nbatches, nblocks); + + } else if (Nz == 512) { + constexpr int FFTS_PER_BLOCK = 1; + constexpr int THREADS_PER_FFT = 512; + + dim3 block(THREADS_PER_FFT, FFTS_PER_BLOCK); + dim3 grid(total_ffts); + + fft_block_cooperative<512, FFTS_PER_BLOCK> + <<>>(in, out, phs, nbatches, nblocks); + } else { + throw std::runtime_error("Unsupported Nz " + std::to_string(Nz) + " for block FFT"); + } + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) { + throw std::runtime_error(std::string("Block FFT failed: ") + cudaGetErrorString(err)); + } +} +#endif + void ShiftedMetric::calcParallelSlices(Field3D& f) { if (f.getDirectionY() == YDirectionType::Aligned) { // Cannot calculate parallel slices for field-aligned fields, so return without @@ -231,9 +491,76 @@ void ShiftedMetric::calcParallelSlices(Field3D& f) { f.splitParallelSlices(); +#if BOUT_HAS_CUDA + auto& region = mesh.getRegion2D("RGN_NOY"); + static size_t nblocks = region.getBlocks().size(); + if (nblocks != region.getBlocks().size()) { + throw BoutException("Number of blocks changed in ShiftedMetric::calcParallelSlices"); + } + + static struct StreamRAII { + cudaStream_t stream = 0; + StreamRAII() { + if (cudaStreamCreate(&stream) != cudaSuccess) { + throw BoutException("Failed to create CUDA stream"); + } + } + + cudaStream_t get() const { return stream; } + + void synchronize() const { cudaStreamSynchronize(stream); } + + ~StreamRAII() { cudaStreamDestroy(stream); } + } stream; + + // Vector of Arrays for each phase. + std::vector> blocks_in_phase; + std::vector> blocks_out_phase; + std::vector> phs_in_phase; + for (const auto& phase : parallel_slice_phases) { auto& f_slice = f.ynext(phase.y_offset); f_slice.allocate(); + + size_t block_idx = 0; + int nbatches = + region.getBlocks().cbegin()->second.ind - region.getBlocks().cbegin()->first.ind; + + Array& blocks_in = blocks_in_phase.emplace_back(nblocks); + Array& blocks_out = blocks_out_phase.emplace_back(nblocks); + Array& phs_in = phs_in_phase.emplace_back(nblocks); + + for (auto block = region.getBlocks().cbegin(), end = region.getBlocks().cend(); + block < end; ++block) { + auto idx_s = block->first; + auto idx_e = block->second; + int inner_nbatches = idx_e.ind - idx_s.ind; + if (inner_nbatches != nbatches) { + throw BoutException( + "Non-uniform number of batches in ShiftedMetric::calcParallelSlices"); + } + const int ix = idx_s.x(); + const int iy = idx_s.y(); + const int iy_offset = iy + phase.y_offset; + + blocks_in[block_idx] = &f(ix, iy_offset, 0); + blocks_out[block_idx] = &f_slice(ix, iy_offset, 0); + phs_in[block_idx] = reinterpret_cast(&phase.phase_shift(ix, iy, 0)); + + block_idx++; + } + + shiftZ_block_fft(mesh.LocalNz, &blocks_in[0], &blocks_out[0], &phs_in[0], nblocks, + nbatches, stream.get()); + } + + // Synchronize to ensure all shifts are complete. + stream.synchronize(); +#else + for (const auto& phase : parallel_slice_phases) { + auto& f_slice = f.ynext(phase.y_offset); + f_slice.allocate(); + BOUT_FOR(i, mesh.getRegion2D("RGN_NOY")) { const int ix = i.x(); const int iy = i.y(); @@ -242,6 +569,7 @@ void ShiftedMetric::calcParallelSlices(Field3D& f) { &(f_slice(ix, iy_offset, 0))); } } +#endif } std::vector diff --git a/src/mesh/parallel/shiftedmetricinterp.cxx b/src/mesh/parallel/shiftedmetricinterp.cxx index 7f3637e79c..de583ceee2 100644 --- a/src/mesh/parallel/shiftedmetricinterp.cxx +++ b/src/mesh/parallel/shiftedmetricinterp.cxx @@ -27,21 +27,34 @@ * **************************************************************************/ +#include +#include + #include "shiftedmetricinterp.hxx" + +#include "bout/boundary_region_iter.hxx" +#include "bout/boutexception.hxx" #include "bout/constants.hxx" -#include "bout/parallel_boundary_region.hxx" +#include "bout/field3d.hxx" ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, Field2D zShift_in, BoutReal zlength_in, Options* opt) : ParallelTransform(mesh, opt), location(location_in), zShift(std::move(zShift_in)), zlength(zlength_in), ydown_index(mesh.ystart) { + + if (mesh.getNZPE() > 1) { + throw BoutException("ShiftedMetricInterp only works with 1 processor in Z"); + } + // check the coordinate system used for the grid data source ShiftedMetricInterp::checkInputGrid(); // Allocate space for interpolator cache: y-guard cells in each direction parallel_slice_interpolators.resize(mesh.ystart * 2); + const BoutReal z_factor = static_cast(mesh.GlobalNzNoBoundaries) / zlength; + // Create the Interpolation objects and set whether they go up or down the // magnetic field auto& interp_options = options["zinterpolation"]; @@ -60,16 +73,16 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, // Find the index positions where the magnetic field line intersects the x-z plane // y_offset points up - Field3D zt_prime_up(&mesh), zt_prime_down(&mesh); + Field3D zt_prime_up(&mesh); + Field3D zt_prime_down(&mesh); zt_prime_up.allocate(); zt_prime_down.allocate(); for (const auto& i : zt_prime_up.getRegion(RGN_NOY)) { // Field line moves in z by an angle zShift(i,j+1)-zShift(i,j) when going // from j to j+1, but we want the shift in index-space - zt_prime_up[i] = static_cast(i.z()) - + (zShift[i.yp(y_offset + 1)] - zShift[i]) - * static_cast(mesh.GlobalNz) / zlength; + zt_prime_up[i] = static_cast(mesh.getGlobalZIndexNoBoundaries(i.z())) + + ((zShift[i.yp(y_offset + 1)] - zShift[i]) * z_factor); } parallel_slice_interpolators[yup_index + y_offset]->calcWeights(zt_prime_up); @@ -77,9 +90,8 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, for (const auto& i : zt_prime_down.getRegion(RGN_NOY)) { // Field line moves in z by an angle -(zShift(i,j)-zShift(i,j-1)) when going // from j to j-1, but we want the shift in index-space - zt_prime_down[i] = static_cast(i.z()) - - (zShift[i] - zShift[i.ym(y_offset + 1)]) - * static_cast(mesh.GlobalNz) / zlength; + zt_prime_down[i] = static_cast(mesh.getGlobalZIndexNoBoundaries(i.z())) + - ((zShift[i] - zShift[i.ym(y_offset + 1)]) * z_factor); } parallel_slice_interpolators[ydown_index + y_offset]->calcWeights(zt_prime_down); @@ -91,15 +103,16 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, interp_from_aligned = ZInterpolationFactory::getInstance().create(&interp_options, 0, &mesh); - Field3D zt_prime_to(&mesh), zt_prime_from(&mesh); + Field3D zt_prime_to(&mesh); + Field3D zt_prime_from(&mesh); zt_prime_to.allocate(); zt_prime_from.allocate(); for (const auto& i : zt_prime_to) { // Field line moves in z by an angle zShift(i,j) when going // from y0 to y(j), but we want the shift in index-space - zt_prime_to[i] = static_cast(i.z()) - + zShift[i] * static_cast(mesh.GlobalNz) / zlength; + zt_prime_to[i] = static_cast(mesh.getGlobalZIndexNoBoundaries(i.z())) + + (zShift[i] * z_factor); } interp_to_aligned->calcWeights(zt_prime_to); @@ -108,83 +121,82 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, // Field line moves in z by an angle zShift(i,j) when going // from y0 to y(j), but we want the shift in index-space. // Here we reverse the shift, so subtract zShift - zt_prime_from[i] = static_cast(i.z()) - - zShift[i] * static_cast(mesh.GlobalNz) / zlength; + zt_prime_from[i] = static_cast(mesh.getGlobalZIndexNoBoundaries(i.z())) + - (zShift[i] * z_factor); } interp_from_aligned->calcWeights(zt_prime_from); - int yvalid = mesh.LocalNy - 2 * mesh.ystart; - // avoid overflow - no stencil need more than 5 points - if (yvalid > 20) { - yvalid = 20; - } + // avoid overflow - no stencil needs more than 5 points + const auto yvalid = + static_cast(std::min(mesh.LocalNy - (2 * mesh.ystart), 20)); + // Create regions for parallel boundary conditions Field2D dy; mesh.get(dy, "dy", 1.); - auto forward_boundary_xin = std::make_shared( + auto forward_boundary_xin = std::make_shared( "parallel_forward_xin", BNDRY_PAR_FWD_XIN, +1, &mesh); for (auto it = mesh.iterateBndryUpperY(); not it.isDone(); it.next()) { for (int z = mesh.zstart; z <= mesh.zend; z++) { forward_boundary_xin->add_point( it.ind, mesh.yend, z, - mesh.GlobalX(it.ind), // x - 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y - zlength * BoutReal(z) / BoutReal(mesh.GlobalNz) // z - + 0.5 * (zShift(it.ind, mesh.yend + 1) - zShift(it.ind, mesh.yend)), + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y + (zlength * mesh.GlobalZ(z)) // z + + (0.5 * (zShift(it.ind, mesh.yend + 1) - zShift(it.ind, mesh.yend))), 0.25 * (1 // dy/2 + dy(it.ind, mesh.yend + 1) / dy(it.ind, mesh.yend)), // length - yvalid); + yvalid, 1); } } - auto backward_boundary_xin = std::make_shared( + auto backward_boundary_xin = std::make_shared( "parallel_backward_xin", BNDRY_PAR_BKWD_XIN, -1, &mesh); for (auto it = mesh.iterateBndryLowerY(); not it.isDone(); it.next()) { for (int z = mesh.zstart; z <= mesh.zend; z++) { backward_boundary_xin->add_point( it.ind, mesh.ystart, z, - mesh.GlobalX(it.ind), // x - 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y - zlength * BoutReal(z) / BoutReal(mesh.GlobalNz) // z - + 0.5 * (zShift(it.ind, mesh.ystart) - zShift(it.ind, mesh.ystart - 1)), + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y + (zlength * mesh.GlobalZ(z)) // z + + (0.5 * (zShift(it.ind, mesh.ystart) - zShift(it.ind, mesh.ystart - 1))), 0.25 * (1 // dy/2 + dy(it.ind, mesh.ystart - 1) / dy(it.ind, mesh.ystart)), - yvalid); + yvalid, -1); } } // Create regions for parallel boundary conditions - auto forward_boundary_xout = std::make_shared( + auto forward_boundary_xout = std::make_shared( "parallel_forward_xout", BNDRY_PAR_FWD_XOUT, +1, &mesh); for (auto it = mesh.iterateBndryUpperY(); not it.isDone(); it.next()) { for (int z = mesh.zstart; z <= mesh.zend; z++) { forward_boundary_xout->add_point( it.ind, mesh.yend, z, - mesh.GlobalX(it.ind), // x - 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y - zlength * BoutReal(z) / BoutReal(mesh.GlobalNz) // z - + 0.5 * (zShift(it.ind, mesh.yend + 1) - zShift(it.ind, mesh.yend)), + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y + (zlength * mesh.GlobalZ(z)) // z + + (0.5 * (zShift(it.ind, mesh.yend + 1) - zShift(it.ind, mesh.yend))), 0.25 * (1 // dy/2 + dy(it.ind, mesh.yend + 1) / dy(it.ind, mesh.yend)), - yvalid); + yvalid, 1); } } - auto backward_boundary_xout = std::make_shared( + auto backward_boundary_xout = std::make_shared( "parallel_backward_xout", BNDRY_PAR_BKWD_XOUT, -1, &mesh); for (auto it = mesh.iterateBndryLowerY(); not it.isDone(); it.next()) { for (int z = mesh.zstart; z <= mesh.zend; z++) { backward_boundary_xout->add_point( it.ind, mesh.ystart, z, - mesh.GlobalX(it.ind), // x - 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y - zlength * BoutReal(z) / BoutReal(mesh.GlobalNz) // z - + 0.5 * (zShift(it.ind, mesh.ystart) - zShift(it.ind, mesh.ystart - 1)), + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y + (zlength * mesh.GlobalZ(z)) // z + + (0.5 * (zShift(it.ind, mesh.ystart) - zShift(it.ind, mesh.ystart - 1))), 0.25 * (dy(it.ind, mesh.ystart - 1) / dy(it.ind, mesh.ystart) // dy/2 + 1), - yvalid); + yvalid, -1); } } @@ -204,14 +216,13 @@ void ShiftedMetricInterp::checkInputGrid() { "Should be 'orthogonal'."); } } // else: coordinate_system variable not found in grid input, indicates older input - // file so must rely on the user having ensured the type is correct + // file so must rely on the user having ensured the type is correct } /*! * Calculate the Y up and down fields */ void ShiftedMetricInterp::calcParallelSlices(Field3D& f) { - AUTO_TRACE(); // Ensure that yup and ydown are different fields f.splitParallelSlices(); diff --git a/src/mesh/parallel_boundary_op.cxx b/src/mesh/parallel_boundary_op.cxx index ebd9852791..dc376e7e63 100644 --- a/src/mesh/parallel_boundary_op.cxx +++ b/src/mesh/parallel_boundary_op.cxx @@ -1,21 +1,48 @@ #include "bout/parallel_boundary_op.hxx" +#include "bout/boundary_region_iter.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" #include "bout/constants.hxx" #include "bout/field_factory.hxx" #include "bout/globals.hxx" #include "bout/mesh.hxx" #include "bout/output.hxx" -BoutReal BoundaryOpPar::getValue(const BoundaryRegionPar& bndry, BoutReal t) { - BoutReal value; - +BoutReal BoundaryOpPar::getValue(const bout::boundary::BoundaryRegionIterFCI& bndry, + BoutReal t) { switch (value_type) { case ValueType::GEN: return gen_values->generate(bout::generator::Context( - bndry.s_x(), bndry.s_y(), bndry.s_z(), CELL_CENTRE, bndry.localmesh, t)); + bndry.s_x(), bndry.s_y(), bndry.s_z(), CELL_CENTRE, bndry.localmesh(), t)); + case ValueType::FIELD: + // FIXME: Interpolate to s_x, s_y, s_z... + return (*field_values)[bndry.ind()]; + case ValueType::REAL: + return real_value; + default: + throw BoutException("Invalid value_type encountered in BoundaryOpPar::getValue"); + } +} + +BoutReal BoundaryOpPar::getValue(const bout::boundary::BoundaryRegionIterX& bndry, + [[maybe_unused]] BoutReal t) { + switch (value_type) { + case ValueType::FIELD: + // FIXME: Interpolate to s_x, s_y, s_z... + return (*field_values)[bndry.ind()]; + case ValueType::REAL: + return real_value; + default: + throw BoutException("Invalid value_type encountered in BoundaryOpPar::getValue"); + } +} + +BoutReal BoundaryOpPar::getValue(const bout::boundary::BoundaryRegionIterY& bndry, + [[maybe_unused]] BoutReal t) { + switch (value_type) { case ValueType::FIELD: // FIXME: Interpolate to s_x, s_y, s_z... - value = (*field_values)[bndry.ind()]; - return value; + return (*field_values)[bndry.ind()]; case ValueType::REAL: return real_value; default: diff --git a/src/mesh/parallel_boundary_stencil.cxx.py b/src/mesh/parallel_boundary_stencil.cxx.py index d0988ee099..adc163eece 100644 --- a/src/mesh/parallel_boundary_stencil.cxx.py +++ b/src/mesh/parallel_boundary_stencil.cxx.py @@ -26,18 +26,10 @@ def run(cmd): if __name__ == "__main__": with tmpf("w", dir=".", delete=False) as f: - f.write("namespace {\n") + f.write("namespace bout::parallel_stencil {\n") f.write( - """ -inline BoutReal pow(BoutReal val, int exp) { - //constexpr int expval = exp; - //static_assert(expval == 2 or expval == 3, "This pow is only for exponent 2 or 3"); - if (exp == 2) { - return val * val; - } - ASSERT3(exp == 3); - return val * val * val; -} + """using std::pow; + """ ) diff --git a/src/mesh/tokamak_coordinates.cxx b/src/mesh/tokamak_coordinates.cxx new file mode 100644 index 0000000000..af8b6335bf --- /dev/null +++ b/src/mesh/tokamak_coordinates.cxx @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include + +namespace bout { +TokamakCoordinates set_tokamak_coordinates(Mesh& mesh, BoutReal Lbar, BoutReal Bbar, + bool no_shear, BoutReal shear_factor) { + Field2D Rxy; + mesh.get(Rxy, "Rxy"); // [m] + Rxy /= Lbar; + + Field2D Zxy; + mesh.get(Zxy, "Zxy"); // [m] + Zxy /= Lbar; + + Field2D Bpxy; + mesh.get(Bpxy, "Bpxy"); // [T] + Bpxy /= Bbar; + + Field2D Btxy; + mesh.get(Btxy, "Btxy"); // [T] + Btxy /= Bbar; + + Field2D Bxy; + mesh.get(Bxy, "Bxy"); // [T] + Bxy /= Bbar; + + Field2D hthe; + mesh.get(hthe, "hthe"); // [m / radian] + hthe /= Lbar; + + Coordinates::FieldMetric I; + if (no_shear) { + I = 0.0; + } else { + mesh.get(I, "sinty"); // [m^-2 T^-1] + } + const auto I_unnormalised = I; + I *= Lbar * Lbar * Bbar * shear_factor; + + Coordinates::FieldMetric dx; + if (mesh.get(dx, "dpsi") != 0) { + dx = mesh.getCoordinates()->dx; + } + dx /= Lbar * Lbar * Bbar; + + const BoutReal sign_of_bp = min(Bpxy, true) < 0.0 ? -1.0 : 1.0; + + auto* coords = mesh.getCoordinates(); + + coords->Bxy = Bxy; + coords->dx = dx; + + coords->g11 = SQ(Rxy * Bpxy); + coords->g22 = 1.0 / SQ(hthe); + coords->g33 = SQ(I) * coords->g11 + SQ(Bxy) / coords->g11; + coords->g12 = 0.0; + coords->g13 = -I * coords->g11; + coords->g23 = -sign_of_bp * Btxy / (hthe * Bpxy * Rxy); + + coords->J = hthe / Bpxy; + + coords->g_11 = 1.0 / coords->g11 + SQ(I * Rxy); + coords->g_22 = SQ(Bxy * hthe / Bpxy); + coords->g_33 = Rxy * Rxy; + coords->g_12 = sign_of_bp * Btxy * hthe * I * Rxy / Bpxy; + coords->g_13 = I * Rxy * Rxy; + coords->g_23 = sign_of_bp * Btxy * hthe * Rxy / Bpxy; + + coords->geometry(); + + return {Rxy, Zxy, Bpxy, Btxy, Bxy, hthe, I, I_unnormalised}; +} +} // namespace bout diff --git a/src/physics/physicsmodel.cxx b/src/physics/physicsmodel.cxx index f4be3bde2a..6cdd78e351 100644 --- a/src/physics/physicsmodel.cxx +++ b/src/physics/physicsmodel.cxx @@ -34,12 +34,14 @@ #include "bout/version.hxx" #include +#include #include #include #include #include +#include #include using namespace std::literals; @@ -67,9 +69,10 @@ PhysicsModel::PhysicsModel() .withDefault(true)), restart_enabled(Options::root()["restart_files"]["enabled"] .doc("Write restart files") - .withDefault(true)) - -{ + .withDefault(true)), + flush_frequency(Options::root()["output"]["flush_frequency"] + .doc("How often to flush to disk") + .withDefault(1)) { if (output_enabled) { output_file = bout::OptionsIOFactory::getInstance().createOutput(); } @@ -164,7 +167,6 @@ void PhysicsModel::bout_solve(Vector3D& var, const char* name, } int PhysicsModel::postInit(bool restarting) { - TRACE("PhysicsModel::postInit"); if (restarting) { solver->readEvolvingVariablesFromOptions(restart_options); @@ -189,7 +191,7 @@ int PhysicsModel::postInit(bool restarting) { } void PhysicsModel::outputVars(Options& options) { - Timer time("io"); + const Timer time("io"); for (const auto& item : dump.getData()) { bout::utils::visit(bout::OptionsConversionVisitor{options, item.name}, item.value); if (item.repeat) { @@ -199,7 +201,7 @@ void PhysicsModel::outputVars(Options& options) { } void PhysicsModel::restartVars(Options& options) { - Timer time("io"); + const Timer time("io"); for (const auto& item : restart.getData()) { bout::utils::visit(bout::OptionsConversionVisitor{options, item.name}, item.value); if (item.repeat) { @@ -217,9 +219,7 @@ void PhysicsModel::writeRestartFile() { void PhysicsModel::writeOutputFile() { writeOutputFile(output_options); } void PhysicsModel::writeOutputFile(const Options& options) { - if (output_enabled) { - output_file->write(options, "t"); - } + writeOutputFile(options, "t"); } void PhysicsModel::writeOutputFile(const Options& options, @@ -230,13 +230,19 @@ void PhysicsModel::writeOutputFile(const Options& options, } void PhysicsModel::finishOutputTimestep() const { - if (output_enabled) { + const Timer timer("io"); + + if (output_enabled and (flush_counter % flush_frequency == 0)) { + output_file->flush(); output_file->verifyTimesteps(); } } int PhysicsModel::PhysicsModelMonitor::call(Solver* solver, BoutReal simtime, int iteration, int nout) { + + model->setFlushCounter(static_cast(iteration)); + // Restart file variables solver->outputVars(model->restart_options, false); model->restartVars(model->restart_options); @@ -255,5 +261,8 @@ int PhysicsModel::PhysicsModelMonitor::call(Solver* solver, BoutReal simtime, model->outputVars(model->output_options); model->writeOutputFile(); + // Reset output options, this avoids rewriting time-independent data + model->output_options = Options{}; + return monitor_result; } diff --git a/src/physics/smoothing.cxx b/src/physics/smoothing.cxx index 0a1391907f..1b437b4352 100644 --- a/src/physics/smoothing.cxx +++ b/src/physics/smoothing.cxx @@ -37,7 +37,6 @@ #include #include #include -#include #include #include @@ -46,7 +45,6 @@ // Smooth using simple 1-2-1 filter const Field3D smooth_x(const Field3D& f) { - TRACE("smooth_x"); Mesh* mesh = f.getMesh(); Field3D result{emptyFrom(f)}; @@ -76,7 +74,6 @@ const Field3D smooth_x(const Field3D& f) { } const Field3D smooth_y(const Field3D& f) { - TRACE("smooth_y"); Mesh* mesh = f.getMesh(); Field3D result{emptyFrom(f)}; @@ -117,7 +114,6 @@ const Field3D smooth_y(const Field3D& f) { so no processor/branch cuts in X */ const Field2D averageX(const Field2D& f) { - TRACE("averageX(Field2D)"); Mesh* mesh = f.getMesh(); int ngx = mesh->LocalNx; @@ -176,8 +172,6 @@ const Field2D averageX(const Field2D& f) { */ const Field3D averageX(const Field3D& f) { - TRACE("averageX(Field3D)"); - Mesh* mesh = f.getMesh(); int ngx = mesh->LocalNx; @@ -230,8 +224,6 @@ const Field3D averageX(const Field3D& f) { } const Field2D averageY(const Field2D& f) { - TRACE("averageY(Field2D)"); - Mesh* mesh = f.getMesh(); int ngx = mesh->LocalNx; int ngy = mesh->LocalNy; @@ -275,8 +267,6 @@ const Field2D averageY(const Field2D& f) { } const Field3D averageY(const Field3D& f) { - TRACE("averageY(Field3D)"); - Mesh* mesh = f.getMesh(); int ngx = mesh->LocalNx; @@ -350,9 +340,9 @@ BoutReal Average_XY(const Field2D& var) { return Vol_Glb; } -BoutReal Vol_Integral(const Field2D& var) { +BoutReal Vol_Integral([[maybe_unused]] const Field2D& var) { #if BOUT_USE_METRIC_3D - AUTO_TRACE(); + throw BoutException("Vol_Intregral currently incompatible with 3D metrics"); #else Mesh* mesh = var.getMesh(); @@ -431,7 +421,7 @@ void nl_filter(rvec& f, BoutReal w) { } const Field3D nl_filter_x(const Field3D& f, BoutReal w) { - TRACE("nl_filter_x( Field3D )"); + Mesh* mesh = f.getMesh(); Field3D result{emptyFrom(f)}; @@ -453,7 +443,6 @@ const Field3D nl_filter_x(const Field3D& f, BoutReal w) { } const Field3D nl_filter_y(const Field3D& f, BoutReal w) { - TRACE("nl_filter_x( Field3D )"); Mesh* mesh = f.getMesh(); @@ -481,7 +470,6 @@ const Field3D nl_filter_y(const Field3D& f, BoutReal w) { } const Field3D nl_filter_z(const Field3D& fs, BoutReal w) { - TRACE("nl_filter_z( Field3D )"); Mesh* mesh = fs.getMesh(); Field3D result{emptyFrom(fs)}; diff --git a/src/physics/sourcex.cxx b/src/physics/sourcex.cxx index 1cfc2fdc6e..659abbf409 100644 --- a/src/physics/sourcex.cxx +++ b/src/physics/sourcex.cxx @@ -2,15 +2,13 @@ * radial source and mask operators **************************************************************/ -#include -#include - #include +#include #include -#include #include +#include -#include "bout/unused.hxx" +#include BoutReal TanH(BoutReal a) { BoutReal temp = exp(a); @@ -77,7 +75,6 @@ const Field3D sink_tanhx(const Field2D& UNUSED(f0), const Field3D& f, BoutReal s // create radial buffer zones to set jpar zero near radial boundaries const Field3D mask_x(const Field3D& f, bool UNUSED(BoutRealspace)) { - TRACE("mask_x"); Mesh* localmesh = f.getMesh(); @@ -101,7 +98,6 @@ const Field3D mask_x(const Field3D& f, bool UNUSED(BoutRealspace)) { // create radial buffer zones to set jpar zero near radial boundaries const Field3D sink_tanhxl(const Field2D& UNUSED(f0), const Field3D& f, BoutReal swidth, BoutReal slength, bool UNUSED(BoutRealspace)) { - TRACE("sink_tanhx"); Mesh* localmesh = f.getMesh(); @@ -123,7 +119,6 @@ const Field3D sink_tanhxl(const Field2D& UNUSED(f0), const Field3D& f, BoutReal // create radial buffer zones to set jpar zero near radial boundaries const Field3D sink_tanhxr(const Field2D& UNUSED(f0), const Field3D& f, BoutReal swidth, BoutReal slength, bool UNUSED(BoutRealspace)) { - TRACE("sink_tanhxr"); Mesh* localmesh = f.getMesh(); @@ -144,7 +139,6 @@ const Field3D sink_tanhxr(const Field2D& UNUSED(f0), const Field3D& f, BoutReal // create radial buffer zones to damp Psi to zero near radial boundaries const Field3D buff_x(const Field3D& f, bool UNUSED(BoutRealspace)) { - TRACE("buff_x"); Mesh* localmesh = f.getMesh(); diff --git a/src/solver/impls/adams_bashforth/adams_bashforth.cxx b/src/solver/impls/adams_bashforth/adams_bashforth.cxx index 79161fcdbf..0cd39a5b6d 100644 --- a/src/solver/impls/adams_bashforth/adams_bashforth.cxx +++ b/src/solver/impls/adams_bashforth/adams_bashforth.cxx @@ -4,17 +4,11 @@ #include #include -#include -#include - #include -#include - namespace { BoutReal lagrange_at_position_denominator(const std::deque& grid, const int position, const int order) { - AUTO_TRACE(); const auto xj = grid[position]; @@ -28,7 +22,7 @@ BoutReal lagrange_at_position_denominator(const std::deque& grid, BoutReal lagrange_at_position_numerator(const BoutReal varX, const std::deque& grid, const int position, const int order) { - AUTO_TRACE(); + BoutReal result = 1.0; for (int i = 0; i < order; i++) { result *= (i != position) ? (varX - grid[i]) : 1.0; @@ -55,7 +49,7 @@ BoutReal lagrange_interpolate(BoutReal start, BoutReal end, BoutReal integrate_lagrange_curve_nc9(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 9; constexpr BoutReal fac = 4.0 / 14175.0; constexpr std::array facs{989.0 * fac, 5888.0 * fac, -928.0 * fac, @@ -68,7 +62,7 @@ BoutReal integrate_lagrange_curve_nc9(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc8(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 8; constexpr BoutReal fac = 7.0 / 17280.0; constexpr std::array facs{751.0 * fac, 3577.0 * fac, 1323.0 * fac, @@ -81,7 +75,7 @@ BoutReal integrate_lagrange_curve_nc8(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc7(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 7; constexpr BoutReal fac = 1.0 / 140.0; constexpr std::array facs{41.0 * fac, 216.0 * fac, 27.0 * fac, @@ -94,7 +88,7 @@ BoutReal integrate_lagrange_curve_nc7(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc6(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 6; constexpr BoutReal fac = 5.0 / 288.0; constexpr std::array facs{19.0 * fac, 75.0 * fac, 50.0 * fac, @@ -106,7 +100,7 @@ BoutReal integrate_lagrange_curve_nc6(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc5(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 5; constexpr BoutReal fac = 2.0 / 45.0; constexpr std::array facs{7.0 * fac, 32.0 * fac, 12.0 * fac, 32.0 * fac, @@ -118,7 +112,7 @@ BoutReal integrate_lagrange_curve_nc5(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc4(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 4; constexpr BoutReal fac = 3.0 / 8.0; constexpr std::array facs{1.0 * fac, 3.0 * fac, 3.0 * fac, 1.0 * fac}; @@ -129,7 +123,7 @@ BoutReal integrate_lagrange_curve_nc4(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc3(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 3; constexpr BoutReal fac = 1.0 / 3.0; constexpr std::array facs{1.0 * fac, 4.0 * fac, 1.0 * fac}; @@ -140,7 +134,7 @@ BoutReal integrate_lagrange_curve_nc3(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc2(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 2; constexpr BoutReal fac = 1.0 / 2.0; constexpr std::array facs{1.0 * fac, 1.0 * fac}; @@ -151,7 +145,6 @@ BoutReal integrate_lagrange_curve_nc2(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve(const BoutReal start, const BoutReal end, const std::deque& points, const int position, const int order) { - AUTO_TRACE(); switch (order) { case 1: @@ -179,7 +172,7 @@ BoutReal integrate_lagrange_curve(const BoutReal start, const BoutReal end, std::vector get_adams_bashforth_coefficients(const BoutReal nextPoint, const std::deque& points, const int order) { - AUTO_TRACE(); + ASSERT2(static_cast(order) <= points.size()); std::vector result; @@ -235,7 +228,7 @@ BoutReal get_timestep_limit(const BoutReal error, const BoutReal tolerance, /// over all processors. BoutReal get_error(const Array& stateApprox, const Array& stateAccurate) { - AUTO_TRACE(); + BoutReal local_result = 0.0; BoutReal err = 0.0; @@ -287,12 +280,12 @@ AdamsBashforthSolver::AdamsBashforthSolver(Options* options) .withDefault(getOutputTimestep())), timestep( (*options)["timestep"].doc("Starting timestep").withDefault(max_timestep)) { - AUTO_TRACE(); + canReset = true; } void AdamsBashforthSolver::setMaxTimestep(BoutReal dt) { - AUTO_TRACE(); + if (dt > timestep) { return; // Already less than this } @@ -306,8 +299,6 @@ void AdamsBashforthSolver::setMaxTimestep(BoutReal dt) { int AdamsBashforthSolver::init() { - TRACE("Initialising AdamsBashforth solver"); - Solver::init(); output << "\n\tAdams-Bashforth (explicit) multistep solver\n"; @@ -345,7 +336,6 @@ int AdamsBashforthSolver::init() { } void AdamsBashforthSolver::resetInternalFields() { - AUTO_TRACE(); // History and times history.clear(); @@ -360,13 +350,12 @@ void AdamsBashforthSolver::resetInternalFields() { } int AdamsBashforthSolver::run() { - AUTO_TRACE(); // Just for developer diagnostics - int nwasted = 0; - int nwasted_following_fail = 0; + [[maybe_unused]] int nwasted = 0; + [[maybe_unused]] int nwasted_following_fail = 0; - for (int s = 0; s < getNumberOutputSteps(); s++) { + for (int s = 1; s <= getNumberOutputSteps(); s++) { BoutReal target = simtime + getOutputTimestep(); bool running = true; @@ -384,7 +373,7 @@ int AdamsBashforthSolver::run() { // Just for developer diagnostics - set to true when the previous // attempt at a time step failed. - bool previous_fail = false; + [[maybe_unused]] bool previous_fail = false; // Flag to indicate if we want to use a lower order method bool use_lower = false; @@ -499,12 +488,14 @@ int AdamsBashforthSolver::run() { // Be more conservative if we've failed; timestep = 0.9 * dt_lim; +#if CHECK > 4 // For developers if (previous_fail) { nwasted_following_fail++; } previous_fail = true; nwasted++; +#endif } // Ditch last history point if we have enough @@ -563,7 +554,6 @@ int AdamsBashforthSolver::run() { BoutReal AdamsBashforthSolver::take_step(const BoutReal timeIn, const BoutReal dt, const int order, Array& current, Array& result) { - AUTO_TRACE(); Array full_update = AB_integrate(nlocal, timeIn + dt, times, history, order); diff --git a/src/solver/impls/arkode/arkode.cxx b/src/solver/impls/arkode/arkode.cxx index 23883cc043..2035824054 100644 --- a/src/solver/impls/arkode/arkode.cxx +++ b/src/solver/impls/arkode/arkode.cxx @@ -27,9 +27,9 @@ #if BOUT_HAS_ARKODE +#include "../../sundials_nvector_interface.hxx" #include "arkode.hxx" -#include "bout/assert.hxx" #include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" @@ -41,6 +41,7 @@ #include "bout/msg_stack.hxx" #include "bout/options.hxx" #include "bout/output.hxx" +#include "bout/region.hxx" #include "bout/solver.hxx" #include "bout/sundials_backports.hxx" #include "bout/unused.hxx" @@ -57,6 +58,7 @@ #include #include #include +#include #include // NOLINTBEGIN(readability-identifier-length) @@ -72,6 +74,18 @@ int arkode_pre(BoutReal t, N_Vector yy, N_Vector yp, N_Vector rvec, N_Vector zve int arkode_jac(N_Vector v, N_Vector Jv, BoutReal t, N_Vector y, N_Vector fy, void* user_data, N_Vector tmp); + +#if SUNDIALS_VERSION_LESS_THAN(7, 2, 0) +// Shim for backwards compatibility +int ARKodeGetNumRhsEvals(void* arkode_mem, int partition_index, long int* num_rhs_evals) { + long int temp = 0; + if (partition_index == 0) { + return ARKStepGetNumRhsEvals(arkode_mem, num_rhs_evals, &temp); + } else { + return ARKStepGetNumRhsEvals(arkode_mem, &temp, num_rhs_evals); + } +} +#endif } // namespace // NOLINTEND(readability-identifier-length) @@ -140,6 +154,9 @@ ArkodeSolver::ArkodeSolver(Options* opts) use_jacobian((*options)["use_jacobian"] .doc("Use user-supplied Jacobian function") .withDefault(false)), + nvector_type((*options)["nvector"] + .doc("N_Vector backend to use: sundials or manyvector") + .withDefault(NVectorType::Sundials)), #if ARKODE_OPTIMAL_PARAMS_SUPPORT optimize( (*options)["optimize"].doc("Use ARKode optimal parameters").withDefault(false)), @@ -176,11 +193,12 @@ ArkodeSolver::~ArkodeSolver() { **************************************************************************/ int ArkodeSolver::init() { - TRACE("Initialising ARKODE solver"); Solver::init(); + const auto backend = nvector_backend(); output.write("Initialising SUNDIALS' ARKODE solver\n"); + backend.ensure_manyvector_available(); // Calculate number of variables (in generic_solver) const int local_N = getLocalN(); @@ -195,14 +213,10 @@ int ArkodeSolver::init() { output.write("\t3d fields = {:d}, 2d fields = {:d} neq={:d}, local_N={:d}\n", n3Dvars(), n2Dvars(), neq, local_N); - // Allocate memory - uvec = callWithSUNContext(N_VNew_Parallel, suncontext, BoutComm::get(), local_N, neq); - if (uvec == nullptr) { - throw BoutException("SUNDIALS memory allocation failed\n"); - } + output.write("\tUsing {} N_Vector backend\n", backend.backend_name()); - // Put the variables into uvec - save_vars(N_VGetArrayPointer(uvec)); + // Allocate memory + uvec = backend.create_state_vector(local_N, neq); switch (treatment) { case Treatment::ImEx: @@ -229,9 +243,11 @@ int ArkodeSolver::init() { throw BoutException("ARKodeSetUserData failed\n"); } - if (ARKodeSetLinear(arkode_mem, static_cast(set_linear)) - != ARK_SUCCESS) { - throw BoutException("ARKodeSetLinear failed\n"); + if (set_linear) { + constexpr bool is_time_dep = false; + if (ARKodeSetLinear(arkode_mem, is_time_dep) != ARK_SUCCESS) { + throw BoutException("ARKodeSetLinear failed\n"); + } } if (fixed_step) { @@ -415,8 +431,7 @@ int ArkodeSolver::init() { if (hasPreconditioner()) { output.write("\tUsing user-supplied preconditioner\n"); - if (ARKodeSetPreconditioner(arkode_mem, nullptr, arkode_pre) - != ARKLS_SUCCESS) { + if (ARKodeSetPreconditioner(arkode_mem, nullptr, arkode_pre) != ARKLS_SUCCESS) { throw BoutException("ARKodeSetPreconditioner failed\n"); } } else { @@ -494,13 +509,11 @@ int ArkodeSolver::init() { **************************************************************************/ int ArkodeSolver::run() { - TRACE("ArkodeSolver::run()"); - if (!initialised) { throw BoutException("ArkodeSolver not initialised\n"); } - for (int i = 0; i < getNumberOutputSteps(); i++) { + for (int i = 1; i <= getNumberOutputSteps(); i++) { /// Run the solver for one output timestep simtime = run(simtime + getOutputTimestep()); @@ -514,12 +527,13 @@ int ArkodeSolver::run() { } // Get additional diagnostics - long int temp_long_int, temp_long_int2; + long int temp_long_int = 0; ARKodeGetNumSteps(arkode_mem, &temp_long_int); nsteps = int(temp_long_int); - ARKStepGetNumRhsEvals(arkode_mem, &temp_long_int, &temp_long_int2); + ARKodeGetNumRhsEvals(arkode_mem, 0, &temp_long_int); nfe_evals = int(temp_long_int); - nfi_evals = int(temp_long_int2); + ARKodeGetNumRhsEvals(arkode_mem, 1, &temp_long_int); + nfi_evals = int(temp_long_int); if (treatment == Treatment::ImEx or treatment == Treatment::Implicit) { ARKodeGetNumNonlinSolvIters(arkode_mem, &temp_long_int); nniters = int(temp_long_int); @@ -554,6 +568,7 @@ int ArkodeSolver::run() { BoutReal ArkodeSolver::run(BoutReal tout) { TRACE("Running solver: solver::run({:e})", tout); + const auto backend = nvector_backend(); bout::globals::mpi->MPI_Barrier(BoutComm::get()); @@ -588,7 +603,7 @@ BoutReal ArkodeSolver::run(BoutReal tout) { } // Copy variables - load_vars(N_VGetArrayPointer(uvec)); + backend.copy_state_from_vector(uvec); // Call rhs function to get extra variables at this time run_rhs(simtime); // run_diffusive(simtime); @@ -605,11 +620,13 @@ BoutReal ArkodeSolver::run(BoutReal tout) { * Explicit RHS function du = F_E(t, u) **************************************************************************/ -void ArkodeSolver::rhs_e(BoutReal t, BoutReal* udata, BoutReal* dudata) { +void ArkodeSolver::rhs_e(BoutReal t, N_Vector u, N_Vector du) { TRACE("Running RHS: ArkodeSolver::rhs_e({:e})", t); + const auto backend = nvector_backend(); // Load state from udata - load_vars(udata); + backend.copy_state_from_vector(u); + backend.copy_deriv_from_vector(du); // Get the current timestep // Note: ARKodeGetCurrentStep updated too late in older versions @@ -619,63 +636,69 @@ void ArkodeSolver::rhs_e(BoutReal t, BoutReal* udata, BoutReal* dudata) { run_convective(t); // Save derivatives to dudata - save_derivs(dudata); + backend.copy_state_to_vector(u); + backend.copy_deriv_to_vector(du); } /************************************************************************** * Implicit RHS function du = F_I(t, u) **************************************************************************/ -void ArkodeSolver::rhs_i(BoutReal t, BoutReal* udata, BoutReal* dudata) { +void ArkodeSolver::rhs_i(BoutReal t, N_Vector u, N_Vector du) { TRACE("Running RHS: ArkodeSolver::rhs_i({:e})", t); + const auto backend = nvector_backend(); - load_vars(udata); + backend.copy_state_from_vector(u); + backend.copy_deriv_from_vector(du); ARKodeGetLastStep(arkode_mem, &hcur); // Call Implicit RHS function run_diffusive(t); - save_derivs(dudata); + backend.copy_state_to_vector(u); + backend.copy_deriv_to_vector(du); } /************************************************************************** * Full RHS function du = F(t, u) **************************************************************************/ -void ArkodeSolver::rhs(BoutReal t, BoutReal* udata, BoutReal* dudata) { +void ArkodeSolver::rhs(BoutReal t, N_Vector u, N_Vector du) { TRACE("Running RHS: ArkodeSolver::rhs({:e})", t); + const auto backend = nvector_backend(); - load_vars(udata); + backend.copy_state_from_vector(u); + backend.copy_deriv_from_vector(du); ARKodeGetLastStep(arkode_mem, &hcur); // Call Implicit RHS function run_rhs(t); - save_derivs(dudata); + backend.copy_state_to_vector(u); + backend.copy_deriv_to_vector(du); } /************************************************************************** * Preconditioner function **************************************************************************/ -void ArkodeSolver::pre(BoutReal t, BoutReal gamma, BoutReal delta, BoutReal* udata, - BoutReal* rvec, BoutReal* zvec) { +void ArkodeSolver::pre(BoutReal t, BoutReal gamma, BoutReal delta, N_Vector u, + N_Vector rvec, N_Vector zvec) { TRACE("Running preconditioner: ArkodeSolver::pre({:e})", t); + const auto backend = nvector_backend(); const BoutReal tstart = bout::globals::mpi->MPI_Wtime(); if (!hasPreconditioner()) { // Identity (but should never happen) - const auto length = N_VGetLocalLength_Parallel(uvec); - std::copy(rvec, rvec + length, zvec); + N_VScale(1, rvec, zvec); return; } // Load state from udata (as with res function) - load_vars(udata); - - // Load vector to be inverted into F_vars - load_derivs(rvec); + backend.copy_state_from_vector(u); + backend.copy_deriv_from_vector(rvec); runPreconditioner(t, gamma, delta); // Save the solution from F_vars - save_derivs(zvec); + backend.copy_state_to_vector(u); + backend.copy_deriv_to_vector(zvec); pre_Wtime += bout::globals::mpi->MPI_Wtime() - tstart; pre_ncalls++; @@ -685,24 +708,24 @@ void ArkodeSolver::pre(BoutReal t, BoutReal gamma, BoutReal delta, BoutReal* uda * Jacobian-vector multiplication function **************************************************************************/ -void ArkodeSolver::jac(BoutReal t, BoutReal* ydata, BoutReal* vdata, BoutReal* Jvdata) { +void ArkodeSolver::jac(BoutReal t, N_Vector y, N_Vector v, N_Vector Jv) { TRACE("Running Jacobian: ArkodeSolver::jac({:e})", t); + const auto backend = nvector_backend(); if (not hasJacobian()) { throw BoutException("No jacobian function supplied!\n"); } // Load state from ydate - load_vars(ydata); - - // Load vector to be multiplied into F_vars - load_derivs(vdata); + backend.copy_state_from_vector(y); + backend.copy_deriv_from_vector(v); // Call function runJacobian(t); // Save Jv from vars - save_derivs(Jvdata); + backend.copy_state_to_vector(y); + backend.copy_deriv_to_vector(Jv); } /************************************************************************** @@ -712,15 +735,11 @@ void ArkodeSolver::jac(BoutReal t, BoutReal* ydata, BoutReal* vdata, BoutReal* J // NOLINTBEGIN(readability-identifier-length) namespace { int arkode_rhs_explicit(BoutReal t, N_Vector u, N_Vector du, void* user_data) { - - BoutReal* udata = N_VGetArrayPointer(u); - BoutReal* dudata = N_VGetArrayPointer(du); - auto* s = static_cast(user_data); // Calculate RHS function try { - s->rhs_e(t, udata, dudata); + s->rhs_e(t, u, du); } catch (BoutRhsFail& error) { return 1; } @@ -728,15 +747,11 @@ int arkode_rhs_explicit(BoutReal t, N_Vector u, N_Vector du, void* user_data) { } int arkode_rhs_implicit(BoutReal t, N_Vector u, N_Vector du, void* user_data) { - - BoutReal* udata = N_VGetArrayPointer(u); - BoutReal* dudata = N_VGetArrayPointer(du); - auto* s = static_cast(user_data); // Calculate RHS function try { - s->rhs_i(t, udata, dudata); + s->rhs_i(t, u, du); } catch (BoutRhsFail& error) { return 1; } @@ -744,15 +759,11 @@ int arkode_rhs_implicit(BoutReal t, N_Vector u, N_Vector du, void* user_data) { } int arkode_rhs(BoutReal t, N_Vector u, N_Vector du, void* user_data) { - - BoutReal* udata = N_VGetArrayPointer(u); - BoutReal* dudata = N_VGetArrayPointer(du); - auto* s = static_cast(user_data); // Calculate RHS function try { - s->rhs(t, udata, dudata); + s->rhs(t, u, du); } catch (BoutRhsFail& error) { return 1; } @@ -768,14 +779,10 @@ int arkode_bbd_rhs(sunindextype UNUSED(Nlocal), BoutReal t, N_Vector u, N_Vector /// Preconditioner function int arkode_pre(BoutReal t, N_Vector yy, N_Vector UNUSED(yp), N_Vector rvec, N_Vector zvec, BoutReal gamma, BoutReal delta, int UNUSED(lr), void* user_data) { - BoutReal* udata = N_VGetArrayPointer(yy); - BoutReal* rdata = N_VGetArrayPointer(rvec); - BoutReal* zdata = N_VGetArrayPointer(zvec); - auto* s = static_cast(user_data); // Calculate residuals - s->pre(t, gamma, delta, udata, rdata, zdata); + s->pre(t, gamma, delta, yy, rvec, zvec); return 0; } @@ -783,13 +790,9 @@ int arkode_pre(BoutReal t, N_Vector yy, N_Vector UNUSED(yp), N_Vector rvec, N_Ve /// Jacobian-vector multiplication function int arkode_jac(N_Vector v, N_Vector Jv, BoutReal t, N_Vector y, N_Vector UNUSED(fy), void* user_data, N_Vector UNUSED(tmp)) { - BoutReal* ydata = N_VGetArrayPointer(y); ///< System state - BoutReal* vdata = N_VGetArrayPointer(v); ///< Input vector - BoutReal* Jvdata = N_VGetArrayPointer(Jv); ///< Jacobian*vector output - auto* s = static_cast(user_data); - s->jac(t, ydata, vdata, Jvdata); + s->jac(t, y, v, Jv); return 0; } diff --git a/src/solver/impls/arkode/arkode.hxx b/src/solver/impls/arkode/arkode.hxx index 4b5e16bfe6..cc6b21c325 100644 --- a/src/solver/impls/arkode/arkode.hxx +++ b/src/solver/impls/arkode/arkode.hxx @@ -2,10 +2,8 @@ * Interface to ARKODE solver * NOTE: ARKode is currently in beta testing so use with cautious optimism * - * NOTE: Only one solver can currently be compiled in - * ************************************************************************** - * Copyright 2010-2024 BOUT++ contributors + * Copyright 2010-2026 BOUT++ contributors * * Contact: Ben Dudson, dudson2@llnl.gov * @@ -41,6 +39,7 @@ RegisterUnavailableSolver #else +#include "../../sundials_nvector_interface.hxx" #include "bout/bout_enum_class.hxx" #include "bout/bout_types.hxx" #include "bout/region.hxx" @@ -122,12 +121,12 @@ public: BoutReal run(BoutReal tout); // These functions used internally (but need to be public) - void rhs_e(BoutReal t, BoutReal* udata, BoutReal* dudata); - void rhs_i(BoutReal t, BoutReal* udata, BoutReal* dudata); - void rhs(BoutReal t, BoutReal* udata, BoutReal* dudata); - void pre(BoutReal t, BoutReal gamma, BoutReal delta, BoutReal* udata, BoutReal* rvec, - BoutReal* zvec); - void jac(BoutReal t, BoutReal* ydata, BoutReal* vdata, BoutReal* Jvdata); + void rhs_e(BoutReal t, N_Vector u, N_Vector du); + void rhs_i(BoutReal t, N_Vector u, N_Vector du); + void rhs(BoutReal t, N_Vector u, N_Vector du); + void pre(BoutReal t, BoutReal gamma, BoutReal delta, N_Vector u, N_Vector rvec, + N_Vector zvec); + void jac(BoutReal t, N_Vector y, N_Vector v, N_Vector Jv); private: BoutReal hcur; //< Current internal timestep @@ -181,6 +180,8 @@ private: bool rightprec; /// Use user-supplied Jacobian function bool use_jacobian; + /// N_Vector backend to use + NVectorType nvector_type; #if ARKODE_OPTIMAL_PARAMS_SUPPORT /// Use ARKode optimal parameters bool optimize; @@ -200,6 +201,10 @@ private: std::vector& f2dtols, std::vector& f3dtols, bool bndry); + SundialsNVectorInterface nvector_backend() { + return SundialsNVectorInterface(*this, suncontext, nvector_type); + } + /// SPGMR solver structure SUNLinearSolver sun_solver{nullptr}; /// Solver for implicit stages diff --git a/src/solver/impls/cvode/cvode.cxx b/src/solver/impls/cvode/cvode.cxx index f0d42d39bc..2cdeeed9c3 100644 --- a/src/solver/impls/cvode/cvode.cxx +++ b/src/solver/impls/cvode/cvode.cxx @@ -3,7 +3,7 @@ * * ************************************************************************** - * Copyright 2010-2024 BOUT++ contributors + * Copyright 2010-2026 BOUT++ contributors * * Contact: Ben Dudson, dudson2@llnl.gov * @@ -25,11 +25,14 @@ **************************************************************************/ #include "bout/build_defines.hxx" +#include #include "cvode.hxx" #if BOUT_HAS_CVODE +#include "../../sundials_nvector_interface.hxx" + #include "bout/bout_enum_class.hxx" #include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" @@ -42,6 +45,9 @@ #include "bout/msg_stack.hxx" #include "bout/options.hxx" #include "bout/output.hxx" +#include "bout/petsclib.hxx" +#include "bout/region.hxx" +#include "bout/solver.hxx" #include "bout/sundials_backports.hxx" #include "bout/unused.hxx" @@ -51,14 +57,22 @@ #include #include +#include +#include +#include + #include #include +#include #include #include +#include BOUT_ENUM_CLASS(positivity_constraint, none, positive, non_negative, negative, non_positive); +BOUT_ENUM_CLASS(linear_solver, gmres, fgmres, tfqmr, bcgs); + // NOLINTBEGIN(readability-identifier-length) namespace { int cvode_linear_rhs(BoutReal t, N_Vector u, N_Vector du, void* user_data); @@ -110,6 +124,16 @@ CvodeSolver::CvodeSolver(Options* opts) .doc("Maximum number of nonlinear iterations allowed by CVODE before " "reducing timestep.") .withDefault(3)), + lsetup_frequency( + (*options)["cvode_lsetup_frequency"] + .doc("Linear solver setup frequency (CVodeSetLSetupFrequency). " + "0 uses the SUNDIALS default.") + .withDefault(0)), + jac_eval_frequency((*options)["cvode_jac_eval_frequency"] + .doc("Jacobian/preconditioner evaluation frequency " + "(CVodeSetJacEvalFrequency). " + "0 uses the SUNDIALS default.") + .withDefault(0)), apply_positivity_constraints( (*options)["apply_positivity_constraints"] .doc("Use CVODE function CVodeSetConstraints to constrain variables - the " @@ -119,11 +143,20 @@ CvodeSolver::CvodeSolver(Options* opts) "each variable") .withDefault(false)), maxl((*options)["maxl"].doc("Maximum number of linear iterations").withDefault(5)), - use_precon((*options)["use_precon"].doc("Use preconditioner?").withDefault(false)), rightprec((*options)["rightprec"] .doc("Use right preconditioner? Otherwise use left.") .withDefault(false)), use_jacobian((*options)["use_jacobian"].withDefault(false)), + precon_method( + (*options)["cvode_precon_method"] + .doc("Preconditioner to use with CVODE Newton iteration. " + "Choices: none (default), auto, user, petsc, bbd. " + "auto prefers user (if supplied), then petsc (if available), " + "then bbd.") + .withDefault(CvodePreconMethod::none)), + nvector_type((*options)["nvector"] + .doc("N_Vector backend to use: sundials or manyvector") + .withDefault(NVectorType::Sundials)), cvode_nonlinear_convergence_coef( (*options)["cvode_nonlinear_convergence_coef"] .doc("Safety factor used in the nonlinear convergence test") @@ -137,6 +170,12 @@ CvodeSolver::CvodeSolver(Options* opts) has_constraints = false; // This solver doesn't have constraints canReset = true; + if ((*options)["use_precon"].isSet()) { + throw BoutException("solver:use_precon is deprecated for CVODE and is now " + "ignored. Use solver:cvode_precon_method=none to disable " + "preconditioning.\n"); + } + // Add diagnostics to output // Needs to be in constructor not init() because init() is called after // Solver::outputVars() @@ -163,6 +202,23 @@ CvodeSolver::~CvodeSolver() { CVodeFree(&cvode_mem); SUNLinSolFree(sun_solver); SUNNonlinSolFree(nonlinear_solver); +#if BOUT_HAS_PETSC + if (petsc_ksp != nullptr) { + KSPDestroy(&petsc_ksp); + } + if (petsc_r != nullptr) { + VecDestroy(&petsc_r); + } + if (petsc_z != nullptr) { + VecDestroy(&petsc_z); + } + if (petsc_x != nullptr) { + VecDestroy(&petsc_x); + } + if (petsc_f != nullptr) { + VecDestroy(&petsc_f); + } +#endif } } @@ -171,9 +227,10 @@ CvodeSolver::~CvodeSolver() { **************************************************************************/ int CvodeSolver::init() { - TRACE("Initialising CVODE solver"); + const auto backend = nvector_backend(); Solver::init(); + backend.ensure_manyvector_available(); output_progress.write("Initialising SUNDIALS' CVODE solver\n"); @@ -181,23 +238,19 @@ int CvodeSolver::init() { const int local_N = getLocalN(); // Get total problem size - int neq; + int neq{0}; if (bout::globals::mpi->MPI_Allreduce(&local_N, &neq, 1, MPI_INT, MPI_SUM, - BoutComm::get())) { + BoutComm::get()) + != 0) { throw BoutException("Allreduce localN -> GlobalN failed!\n"); } output_info.write("\t3d fields = {:d}, 2d fields = {:d} neq={:d}, local_N={:d}\n", n3Dvars(), n2Dvars(), neq, local_N); + output_info.write("\tUsing {} N_Vector backend\n", backend.backend_name()); // Allocate memory - uvec = callWithSUNContext(N_VNew_Parallel, suncontext, BoutComm::get(), local_N, neq); - if (uvec == nullptr) { - throw BoutException("SUNDIALS memory allocation failed\n"); - } - - // Put the variables into uvec - save_vars(N_VGetArrayPointer(uvec)); + uvec = backend.create_state_vector(local_N, neq); if (adams_moulton) { // By default use functional iteration for Adams-Moulton @@ -269,12 +322,10 @@ int CvodeSolver::init() { return Options::root()[f3.name]["atol"].withDefault(abstol); }); - N_Vector abstolvec = N_VClone(uvec); - if (abstolvec == nullptr) { - throw BoutException("SUNDIALS memory allocation (abstol vector) failed\n"); - } + N_Vector abstolvec = + backend.clone_vector_like(uvec, "SUNDIALS memory allocation (abstol vector)"); - set_vector_option_values(N_VGetArrayPointer(abstolvec), f2dtols, f3dtols); + backend.fill_vector_values(abstolvec, f2dtols, f3dtols); if (CVodeSVtolerances(cvode_mem, reltol, abstolvec) != CV_SUCCESS) { throw BoutException("CVodeSVtolerances failed\n"); @@ -307,18 +358,20 @@ int CvodeSolver::init() { throw BoutException("CVodeSetMaxNonlinIters failed\n"); } +#if SUNDIALS_VERSION_MAJOR >= 6 + if (CVodeSetLSetupFrequency(cvode_mem, lsetup_frequency) != CV_SUCCESS) { + throw BoutException("CVodeSetLSetupFrequency failed\n"); + } +#endif + if (apply_positivity_constraints) { auto f2d_constraints = create_constraints(f2d); auto f3d_constraints = create_constraints(f3d); - N_Vector constraints_vec = N_VClone(uvec); - if (constraints_vec == nullptr) { - throw BoutException("SUNDIALS memory allocation (positivity constraints vector) " - "failed\n"); - } + N_Vector constraints_vec = backend.clone_vector_like( + uvec, "SUNDIALS memory allocation (positivity constraints vector)"); - set_vector_option_values(N_VGetArrayPointer(constraints_vec), f2d_constraints, - f3d_constraints); + backend.fill_vector_values(constraints_vec, f2d_constraints, f3d_constraints); if (CVodeSetConstraints(cvode_mem, constraints_vec) != CV_SUCCESS) { throw BoutException("CVodeSetConstraints failed\n"); @@ -340,11 +393,44 @@ int CvodeSolver::init() { } } else { output_info.write("\tUsing Newton iteration\n"); - TRACE("Setting preconditioner"); - const auto prectype = - use_precon ? (rightprec ? SUN_PREC_RIGHT : SUN_PREC_LEFT) : SUN_PREC_NONE; - sun_solver = callWithSUNContext(SUNLinSol_SPGMR, suncontext, uvec, prectype, maxl); + CvodePreconMethod selected_precon = precon_method; + if (selected_precon == CvodePreconMethod::Auto) { + if (hasPreconditioner()) { + selected_precon = CvodePreconMethod::user; + } else if (bout::build::has_petsc) { + selected_precon = CvodePreconMethod::petsc; + } else { + selected_precon = CvodePreconMethod::bbd; + } + } + + auto prectype = SUN_PREC_NONE; + if (selected_precon != CvodePreconMethod::none) { + if (rightprec) { + prectype = SUN_PREC_RIGHT; + } else { + prectype = SUN_PREC_LEFT; + } + } + + switch ((*options)["linear_solver"] + .doc("Set linear solver type. Default is gmres.") + .withDefault(linear_solver::gmres)) { + case linear_solver::gmres: + sun_solver = callWithSUNContext(SUNLinSol_SPGMR, suncontext, uvec, prectype, maxl); + break; + case linear_solver::fgmres: + sun_solver = callWithSUNContext(SUNLinSol_SPFGMR, suncontext, uvec, prectype, maxl); + break; + case linear_solver::tfqmr: + sun_solver = + callWithSUNContext(SUNLinSol_SPTFQMR, suncontext, uvec, prectype, maxl); + break; + case linear_solver::bcgs: + sun_solver = callWithSUNContext(SUNLinSol_SPBCGS, suncontext, uvec, prectype, maxl); + break; + }; if (sun_solver == nullptr) { throw BoutException("Creating SUNDIALS linear solver failed\n"); } @@ -352,42 +438,94 @@ int CvodeSolver::init() { throw BoutException("CVodeSetLinearSolver failed\n"); } - if (use_precon) { - if (hasPreconditioner()) { - output_info.write("\tUsing user-supplied preconditioner\n"); +#if SUNDIALS_VERSION_MAJOR >= 6 + if (CVodeSetJacEvalFrequency(cvode_mem, jac_eval_frequency) != CVLS_SUCCESS) { + throw BoutException("CVodeSetJacEvalFrequency failed\n"); + } +#endif - if (CVodeSetPreconditioner(cvode_mem, nullptr, cvode_pre) != CVLS_SUCCESS) { - throw BoutException("CVodeSetPreconditioner failed\n"); - } - } else { - output_info.write("\tUsing BBD preconditioner\n"); - - /// Get options - // Compute band_width_default from actually added fields, to allow for multiple - // Mesh objects - // - // Previous implementation was equivalent to: - // int MXSUB = mesh->xend - mesh->xstart + 1; - // int band_width_default = n3Dvars()*(MXSUB+2); - const int band_width_default = std::accumulate( - begin(f3d), end(f3d), 0, [](int a, const VarStr& fvar) { - Mesh* localmesh = fvar.var->getMesh(); - return a + localmesh->xend - localmesh->xstart + 3; - }); - - const auto mudq = (*options)["mudq"].withDefault(band_width_default); - const auto mldq = (*options)["mldq"].withDefault(band_width_default); - const auto mukeep = (*options)["mukeep"].withDefault(n3Dvars() + n2Dvars()); - const auto mlkeep = (*options)["mlkeep"].withDefault(n3Dvars() + n2Dvars()); - - if (CVBBDPrecInit(cvode_mem, local_N, mudq, mldq, mukeep, mlkeep, 0.0, - cvode_bbd_rhs, nullptr) - != CVLS_SUCCESS) { - throw BoutException("CVBBDPrecInit failed\n"); - } - } - } else { + if (selected_precon == CvodePreconMethod::none) { output_info.write("\tNo preconditioning\n"); + + } else if (selected_precon == CvodePreconMethod::user) { + if (!hasPreconditioner()) { + throw BoutException( + "solver:cvode_precon_method=user requested, but no user preconditioner " + "has been supplied."); + } + + output_info.write("\tUsing user-supplied preconditioner\n"); + + if (CVodeSetPreconditioner(cvode_mem, nullptr, cvode_pre) != CVLS_SUCCESS) { + throw BoutException("CVodeSetPreconditioner failed\n"); + } + } else if (selected_precon == CvodePreconMethod::petsc) { +#if !BOUT_HAS_PETSC + throw BoutException( + "solver:cvode_precon_method=petsc requested, but BOUT++ was not " + "configured with PETSc."); +#else + output_info.write("\tUsing PETSc coloring preconditioner\n"); + + if (!petsc_lib) { + petsc_lib = std::make_unique(); + } + + petsc_global_N = neq; + petsc_rhs_tmp.resize(local_N); + + if (petsc_ksp == nullptr) { + PetscCall(KSPCreate(BoutComm::get(), &petsc_ksp)); + PetscCall(KSPSetType(petsc_ksp, KSPPREONLY)); + PetscCall(KSPSetOptionsPrefix(petsc_ksp, "cvode_petscpre_")); + PetscCall(KSPSetFromOptions(petsc_ksp)); + } + + if (petsc_x == nullptr) { + PetscCall(VecCreateMPI(BoutComm::get(), local_N, petsc_global_N, &petsc_x)); + PetscCall(VecDuplicate(petsc_x, &petsc_f)); + PetscCall(VecCreateMPI(BoutComm::get(), local_N, petsc_global_N, &petsc_r)); + PetscCall(VecCreateMPI(BoutComm::get(), local_N, petsc_global_N, &petsc_z)); + } + + Field3D index = globalIndex(0); + PetscCall(petsc_preconditioner.createJacobianPattern( + index, *options, local_N, n2Dvars(), n3Dvars(), BoutComm::get())); + PetscCall( + petsc_preconditioner.updateColoring(CvodeSolver::petscFormFunction, this)); + PetscCall(MatFDColoringSetF(petsc_preconditioner.coloring(), petsc_f)); + + if (CVodeSetPreconditioner(cvode_mem, petscPSetup, petscPSolve) != CVLS_SUCCESS) { + throw BoutException("CVodeSetPreconditioner (PETSc coloring) failed\n"); + } +#endif // BOUT_HAS_PETSC + + } else if (selected_precon == CvodePreconMethod::bbd) { + output_info.write("\tUsing BBD preconditioner\n"); + + /// Get options + // Compute band_width_default from actually added fields, to allow for multiple + // Mesh objects + // + // Previous implementation was equivalent to: + // int MXSUB = mesh->xend - mesh->xstart + 1; + // int band_width_default = n3Dvars()*(MXSUB+2); + const int band_width_default = std::accumulate( + begin(f3d), end(f3d), 0, [](int a, const VarStr& fvar) { + const Mesh* localmesh = fvar.var->getMesh(); + return a + localmesh->xend - localmesh->xstart + 3; + }); + + const auto mudq = (*options)["mudq"].withDefault(band_width_default); + const auto mldq = (*options)["mldq"].withDefault(band_width_default); + const auto mukeep = (*options)["mukeep"].withDefault(n3Dvars() + n2Dvars()); + const auto mlkeep = (*options)["mlkeep"].withDefault(n3Dvars() + n2Dvars()); + + if (CVBBDPrecInit(cvode_mem, local_N, mudq, mldq, mukeep, mlkeep, 0.0, + cvode_bbd_rhs, nullptr) + != CVLS_SUCCESS) { + throw BoutException("CVBBDPrecInit failed\n"); + } } /// Set Jacobian-vector multiplication function @@ -463,13 +601,12 @@ CvodeSolver::create_constraints(const std::vector>& fields) { **************************************************************************/ int CvodeSolver::run() { - TRACE("CvodeSolver::run()"); if (!cvode_initialised) { throw BoutException("CvodeSolver not initialised\n"); } - for (int i = 0; i < getNumberOutputSteps(); i++) { + for (int i = 1; i <= getNumberOutputSteps(); i++) { /// Run the solver for one output timestep simtime = run(simtime + getOutputTimestep()); @@ -534,7 +671,7 @@ int CvodeSolver::run() { /// Call the monitor function - if (call_monitors(simtime, i, getNumberOutputSteps())) { + if (call_monitors(simtime, i, getNumberOutputSteps()) != 0) { // User signalled to quit break; } @@ -545,6 +682,7 @@ int CvodeSolver::run() { BoutReal CvodeSolver::run(BoutReal tout) { TRACE("Running solver: solver::run({})", tout); + const auto backend = nvector_backend(); bout::globals::mpi->MPI_Barrier(BoutComm::get()); @@ -561,7 +699,7 @@ BoutReal CvodeSolver::run(BoutReal tout) { CVodeGetCurrentTime(cvode_mem, &internal_time); while (internal_time < tout) { // Run another step - BoutReal last_time = internal_time; + const BoutReal last_time = internal_time; flag = CVode(cvode_mem, tout, uvec, &internal_time, CV_ONE_STEP); if (flag < 0) { @@ -578,7 +716,7 @@ BoutReal CvodeSolver::run(BoutReal tout) { } // Copy variables - load_vars(N_VGetArrayPointer(uvec)); + backend.copy_state_from_vector(uvec); // Call rhs function to get extra variables at this time run_rhs(simtime); @@ -595,11 +733,12 @@ BoutReal CvodeSolver::run(BoutReal tout) { * RHS function du = F(t, u) **************************************************************************/ -void CvodeSolver::rhs(BoutReal t, BoutReal* udata, BoutReal* dudata, bool linear) { +void CvodeSolver::rhs(BoutReal t, N_Vector u, N_Vector du, bool linear) { TRACE("Running RHS: CvodeSolver::res({})", t); + const auto backend = nvector_backend(); // Load state from udata - load_vars(udata); + backend.copy_state_from_vector(u); // Get the current timestep // Note: CVodeGetCurrentStep updated too late in older versions @@ -609,39 +748,55 @@ void CvodeSolver::rhs(BoutReal t, BoutReal* udata, BoutReal* dudata, bool linear run_rhs(t, linear); // Save derivatives to dudata - save_derivs(dudata); + backend.copy_state_to_vector(u); + backend.copy_deriv_to_vector(du); +} + +void CvodeSolver::rhs(BoutReal t, BoutReal* u, BoutReal* du, bool linear) { + TRACE("Running RHS: CvodeSolver::res({})", t); + + // Load state + load_vars(u); + + // Get the current timestep + // Note: CVodeGetCurrentStep updated too late in older versions + CVodeGetLastStep(cvode_mem, &hcur); + + // Call RHS function + run_rhs(t, linear); + + // Save derivatives to du + save_derivs(du); } /************************************************************************** * Preconditioner function **************************************************************************/ -void CvodeSolver::pre(BoutReal t, BoutReal gamma, BoutReal delta, BoutReal* udata, - BoutReal* rvec, BoutReal* zvec) { +void CvodeSolver::pre(BoutReal t, BoutReal gamma, BoutReal delta, N_Vector u, + N_Vector rvec, N_Vector zvec) { TRACE("Running preconditioner: CvodeSolver::pre({})", t); + const auto backend = nvector_backend(); - BoutReal tstart = bout::globals::mpi->MPI_Wtime(); - - const auto length = N_VGetLocalLength_Parallel(uvec); + const BoutReal tstart = bout::globals::mpi->MPI_Wtime(); if (!hasPreconditioner()) { // Identity (but should never happen) - for (int i = 0; i < length; i++) { - zvec[i] = rvec[i]; - } + N_VScale(1.0, rvec, zvec); return; } // Load state from udata (as with res function) - load_vars(udata); + backend.copy_state_from_vector(u); // Load vector to be inverted into F_vars - load_derivs(rvec); + backend.copy_deriv_from_vector(rvec); runPreconditioner(t, gamma, delta); // Save the solution from F_vars - save_derivs(zvec); + backend.copy_state_to_vector(u); + backend.copy_deriv_to_vector(zvec); pre_Wtime += bout::globals::mpi->MPI_Wtime() - tstart; pre_ncalls++; @@ -651,24 +806,26 @@ void CvodeSolver::pre(BoutReal t, BoutReal gamma, BoutReal delta, BoutReal* udat * Jacobian-vector multiplication function **************************************************************************/ -void CvodeSolver::jac(BoutReal t, BoutReal* ydata, BoutReal* vdata, BoutReal* Jvdata) { +void CvodeSolver::jac(BoutReal t, N_Vector y, N_Vector v, N_Vector Jv) { TRACE("Running Jacobian: CvodeSolver::jac({})", t); + const auto backend = nvector_backend(); if (not hasJacobian()) { throw BoutException("No jacobian function supplied!\n"); } // Load state from ydate - load_vars(ydata); + backend.copy_state_from_vector(y); // Load vector to be multiplied into F_vars - load_derivs(vdata); + backend.copy_deriv_from_vector(v); // Call function runJacobian(t); // Save Jv from vars - save_derivs(Jvdata); + backend.copy_state_to_vector(y); + backend.copy_deriv_to_vector(Jv); } /************************************************************************** @@ -678,15 +835,11 @@ void CvodeSolver::jac(BoutReal t, BoutReal* ydata, BoutReal* vdata, BoutReal* Jv // NOLINTBEGIN(readability-identifier-length) namespace { int cvode_linear_rhs(BoutReal t, N_Vector u, N_Vector du, void* user_data) { - - BoutReal* udata = N_VGetArrayPointer(u); - BoutReal* dudata = N_VGetArrayPointer(du); - auto* s = static_cast(user_data); // Calculate RHS function try { - s->rhs(t, udata, dudata, true); + s->rhs(t, u, du, true); } catch (BoutRhsFail& error) { return 1; } @@ -694,15 +847,11 @@ int cvode_linear_rhs(BoutReal t, N_Vector u, N_Vector du, void* user_data) { } int cvode_nonlinear_rhs(BoutReal t, N_Vector u, N_Vector du, void* user_data) { - - BoutReal* udata = N_VGetArrayPointer(u); - BoutReal* dudata = N_VGetArrayPointer(du); - auto* s = static_cast(user_data); // Calculate RHS function try { - s->rhs(t, udata, dudata, false); + s->rhs(t, u, du, false); } catch (BoutRhsFail& error) { return 1; } @@ -718,14 +867,10 @@ int cvode_bbd_rhs(sunindextype UNUSED(Nlocal), BoutReal t, N_Vector u, N_Vector /// Preconditioner function int cvode_pre(BoutReal t, N_Vector yy, N_Vector UNUSED(yp), N_Vector rvec, N_Vector zvec, BoutReal gamma, BoutReal delta, int UNUSED(lr), void* user_data) { - BoutReal* udata = N_VGetArrayPointer(yy); - BoutReal* rdata = N_VGetArrayPointer(rvec); - BoutReal* zdata = N_VGetArrayPointer(zvec); - auto* s = static_cast(user_data); // Calculate residuals - s->pre(t, gamma, delta, udata, rdata, zdata); + s->pre(t, gamma, delta, yy, rvec, zvec); return 0; } @@ -733,70 +878,144 @@ int cvode_pre(BoutReal t, N_Vector yy, N_Vector UNUSED(yp), N_Vector rvec, N_Vec /// Jacobian-vector multiplication function int cvode_jac(N_Vector v, N_Vector Jv, BoutReal t, N_Vector y, N_Vector UNUSED(fy), void* user_data, N_Vector UNUSED(tmp)) { - BoutReal* ydata = N_VGetArrayPointer(y); ///< System state - BoutReal* vdata = N_VGetArrayPointer(v); ///< Input vector - BoutReal* Jvdata = N_VGetArrayPointer(Jv); ///< Jacobian*vector output - auto* s = static_cast(user_data); - s->jac(t, ydata, vdata, Jvdata); + s->jac(t, y, v, Jv); return 0; } } // namespace // NOLINTEND(readability-identifier-length) -/************************************************************************** - * CVODE vector option functions - **************************************************************************/ +#if BOUT_HAS_PETSC +PetscErrorCode CvodeSolver::petscFormFunction(void* UNUSED(dummy), Vec x, Vec f, + void* ctx) { + auto* s = static_cast(ctx); + + PetscInt length = 0; + PetscCall(VecGetLocalSize(x, &length)); + s->petsc_rhs_tmp.resize(static_cast(length)); + + const BoutReal* xdata = nullptr; + PetscCall(VecGetArrayRead(x, &xdata)); -void CvodeSolver::set_vector_option_values(BoutReal* option_data, - std::vector& f2dtols, - std::vector& f3dtols) { - int p = 0; // Counter for location in option_data array + BoutReal* fdata = nullptr; + PetscCall(VecGetArray(f, &fdata)); - // All boundaries - for (const auto& i2d : bout::globals::mesh->getRegion2D("RGN_BNDRY")) { - loop_vector_option_values_op(i2d, option_data, p, f2dtols, f3dtols, true); + try { + s->rhs(s->petsc_t, const_cast(xdata), s->petsc_rhs_tmp.data(), true); + } catch (BoutRhsFail&) { + PetscCall(VecRestoreArrayRead(x, &xdata)); + PetscCall(VecRestoreArray(f, &fdata)); + return 1; } - // Bulk of points - for (const auto& i2d : bout::globals::mesh->getRegion2D("RGN_NOBNDRY")) { - loop_vector_option_values_op(i2d, option_data, p, f2dtols, f3dtols, false); + + for (PetscInt i = 0; i < length; ++i) { + fdata[i] = xdata[i] - (s->petsc_gamma * s->petsc_rhs_tmp[i]); } + + PetscCall(VecRestoreArrayRead(x, &xdata)); + PetscCall(VecRestoreArray(f, &fdata)); + + return PETSC_SUCCESS; } -void CvodeSolver::loop_vector_option_values_op(Ind2D UNUSED(i2d), BoutReal* option_data, - int& p, std::vector& f2dtols, - std::vector& f3dtols, - bool bndry) { - // Loop over 2D variables - for (std::vector::size_type i = 0; i < f2dtols.size(); i++) { - if (bndry && !f2d[i].evolve_bndry) { - continue; - } - option_data[p] = f2dtols[i]; - p++; +int CvodeSolver::petscPSetup(BoutReal t, N_Vector yy, N_Vector UNUSED(yp), + CvodeBool UNUSED(jok), CvodeBool* jcurPtr, BoutReal gamma, + void* user_data) { + auto* s = static_cast(user_data); + + s->petsc_t = t; + s->petsc_gamma = gamma; + + if (s->nvector_type == NVectorType::ManyVector) { + throw BoutException("solver:cvode_precon_method=petsc is not supported with " + "solver:nvector=manyvector"); } - for (int jz = 0; jz < bout::globals::mesh->LocalNz; jz++) { - // Loop over 3D variables - for (std::vector::size_type i = 0; i < f3dtols.size(); i++) { - if (bndry && !f3d[i].evolve_bndry) { - continue; - } - option_data[p] = f3dtols[i]; - p++; - } + auto* ydata = N_VGetArrayPointer(yy); + + PetscErrorCode ierr = VecPlaceArray(s->petsc_x, ydata); + if (ierr != 0) { + return 1; + } + + ierr = petscFormFunction(nullptr, s->petsc_x, s->petsc_f, s); + if (ierr != 0) { + VecResetArray(s->petsc_x); + return 1; } + + Mat J = s->petsc_preconditioner.jacobian(); + ierr = MatZeroEntries(J); + if (ierr != 0) { + VecResetArray(s->petsc_x); + return 1; + } + + ierr = MatFDColoringApply(J, s->petsc_preconditioner.coloring(), s->petsc_x, nullptr); + if (ierr != 0) { + VecResetArray(s->petsc_x); + return 1; + } + + ierr = MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY); + if (ierr == 0) { + ierr = MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY); + } + if (ierr != 0) { + VecResetArray(s->petsc_x); + return 1; + } + + ierr = KSPSetOperators(s->petsc_ksp, J, J); + if (ierr == 0) { + ierr = KSPSetUp(s->petsc_ksp); + } + + VecResetArray(s->petsc_x); + + if (ierr != 0) { + return 1; + } + + if (jcurPtr != nullptr) { + *jcurPtr = 1; + } + + return 0; +} + +int CvodeSolver::petscPSolve(BoutReal UNUSED(t), N_Vector UNUSED(yy), N_Vector UNUSED(yp), + N_Vector rvec, N_Vector zvec, BoutReal UNUSED(gamma), + BoutReal UNUSED(delta), int UNUSED(lr), void* user_data) { + auto* s = static_cast(user_data); + + auto* rdata = N_VGetArrayPointer(rvec); + auto* zdata = N_VGetArrayPointer(zvec); + + PetscErrorCode ierr = VecPlaceArray(s->petsc_r, rdata); + if (ierr == 0) { + ierr = VecPlaceArray(s->petsc_z, zdata); + } + if (ierr == 0) { + ierr = KSPSolve(s->petsc_ksp, s->petsc_r, s->petsc_z); + } + + VecResetArray(s->petsc_r); + VecResetArray(s->petsc_z); + + return ierr == 0 ? 0 : 1; } +#endif void CvodeSolver::resetInternalFields() { - TRACE("CvodeSolver::resetInternalFields"); - save_vars(N_VGetArrayPointer(uvec)); + const auto backend = nvector_backend(); + + backend.copy_state_to_vector(uvec); if (CVodeReInit(cvode_mem, simtime, uvec) != CV_SUCCESS) { throw BoutException("CVodeReInit failed\n"); } } - #endif diff --git a/src/solver/impls/cvode/cvode.hxx b/src/solver/impls/cvode/cvode.hxx index f0cc2eedf8..c242e4128a 100644 --- a/src/solver/impls/cvode/cvode.hxx +++ b/src/solver/impls/cvode/cvode.hxx @@ -4,9 +4,9 @@ * NOTE: Only one solver can currently be compiled in * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -28,8 +28,10 @@ #ifndef BOUT_SUNDIAL_SOLVER_H #define BOUT_SUNDIAL_SOLVER_H +#include "bout/bout_enum_class.hxx" #include "bout/build_defines.hxx" #include "bout/solver.hxx" +#include #if not BOUT_HAS_CVODE @@ -40,10 +42,19 @@ RegisterUnavailableSolver #else +#include "../../sundials_nvector_interface.hxx" #include "bout/bout_types.hxx" #include "bout/region.hxx" #include "bout/sundials_backports.hxx" +#if BOUT_HAS_PETSC +#include "bout/petsc_preconditioner.hxx" +#include "bout/petsclib.hxx" + +#include +#include +#endif + #include #include @@ -54,6 +65,16 @@ namespace { RegisterSolver registersolvercvode("cvode"); } +// Preconditioner selection for CVODE. +// Note: String comparisons are case-insensitive so "Auto" avoids conflict with keyword +BOUT_ENUM_CLASS(CvodePreconMethod, none, Auto, user, petsc, bbd); + +#if SUNDIALS_VERSION_AT_LEAST(6, 0, 0) +using CvodeBool = sunbooleantype; +#else +using CvodeBool = booleantype; +#endif + class CvodeSolver : public Solver { public: explicit CvodeSolver(Options* opts = nullptr); @@ -68,12 +89,23 @@ public: void resetInternalFields() override; // These functions are used internally (but need to be public) - void rhs(BoutReal t, BoutReal* udata, BoutReal* dudata, bool linear); - void pre(BoutReal t, BoutReal gamma, BoutReal delta, BoutReal* udata, BoutReal* rvec, - BoutReal* zvec); - void jac(BoutReal t, BoutReal* ydata, BoutReal* vdata, BoutReal* Jvdata); + void rhs(BoutReal t, N_Vector u, N_Vector du, bool linear); + /// This version will copy data to/from a linear vector into BOUT++ fields + void rhs(BoutReal t, BoutReal* u, BoutReal* du, bool linear); + void pre(BoutReal t, BoutReal gamma, BoutReal delta, N_Vector u, N_Vector rvec, + N_Vector zvec); + void jac(BoutReal t, N_Vector y, N_Vector v, N_Vector Jv); private: +#if BOUT_HAS_PETSC + static PetscErrorCode petscFormFunction(void* dummy, Vec x, Vec f, void* ctx); + static int petscPSetup(BoutReal t, N_Vector yy, N_Vector yp, CvodeBool jok, + CvodeBool* jcurPtr, BoutReal gamma, void* user_data); + static int petscPSolve(BoutReal t, N_Vector yy, N_Vector yp, N_Vector rvec, + N_Vector zvec, BoutReal gamma, BoutReal delta, int lr, + void* user_data); +#endif + BoutReal hcur; //< Current internal timestep bool diagnose{false}; //< Output additional diagnostics @@ -111,17 +143,21 @@ private: /// reducing timestep. CVODE default (used if this option is /// negative) is 3 int max_nonlinear_iterations; + /// Max number of steps between calls to linear solver setup + long int lsetup_frequency; + /// Max number of steps between Jacobian/preconditioner evaluations + long int jac_eval_frequency; /// Use CVODE function CVodeSetConstraints to constrain variables - /// the constraint to be applied is set by the positivity_constraint /// option in the subsection for each variable bool apply_positivity_constraints; /// Maximum number of linear iterations int maxl; - /// Use preconditioner? - bool use_precon; /// Use right preconditioner? Otherwise use left. bool rightprec; bool use_jacobian; + CvodePreconMethod precon_method; + NVectorType nvector_type; BoutReal cvode_nonlinear_convergence_coef; BoutReal cvode_linear_convergence_coef; @@ -139,20 +175,34 @@ private: bool cvode_initialised{false}; - void set_vector_option_values(BoutReal* option_data, std::vector& f2dtols, - std::vector& f3dtols); - void loop_vector_option_values_op(Ind2D i2d, BoutReal* option_data, int& p, - std::vector& f2dtols, - std::vector& f3dtols, bool bndry); template std::vector create_constraints(const std::vector>& fields); + SundialsNVectorInterface nvector_backend() { + return SundialsNVectorInterface(*this, suncontext, nvector_type); + } + /// SPGMR solver structure SUNLinearSolver sun_solver{nullptr}; /// Solver for functional iterations for Adams-Moulton SUNNonlinearSolver nonlinear_solver{nullptr}; /// Context for SUNDIALS memory allocations sundials::Context suncontext; + +#if BOUT_HAS_PETSC + // PETSc-coloring-based preconditioning for CVODE + std::unique_ptr petsc_lib; + PetscPreconditioner petsc_preconditioner; + KSP petsc_ksp{nullptr}; + Vec petsc_r{nullptr}; + Vec petsc_z{nullptr}; + Vec petsc_x{nullptr}; + Vec petsc_f{nullptr}; + PetscInt petsc_global_N{0}; + BoutReal petsc_gamma{0.0}; + BoutReal petsc_t{0.0}; + std::vector petsc_rhs_tmp; +#endif }; #endif // BOUT_HAS_CVODE diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 3976f4402c..599d556c00 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -1,16 +1,16 @@ - #include "euler.hxx" #include #include -#include +#include #include -#include - -#include - +#include #include +#include +#include +#include + EulerSolver::EulerSolver(Options* options) : Solver(options), mxstep((*options)["mxstep"] .doc("Maximum number of steps between outputs") @@ -20,7 +20,10 @@ EulerSolver::EulerSolver(Options* options) .withDefault(2.)), timestep((*options)["timestep"] .doc("Internal timestep (defaults to output timestep)") - .withDefault(getOutputTimestep())) {} + .withDefault(getOutputTimestep())), + dump_at_time((*options)["dump_at_time"] + .doc("Dump debug info about the simulation") + .withDefault(-1)) {} void EulerSolver::setMaxTimestep(BoutReal dt) { if (dt >= cfl_factor * timestep) { @@ -33,7 +36,6 @@ void EulerSolver::setMaxTimestep(BoutReal dt) { } int EulerSolver::init() { - TRACE("Initialising Euler solver"); Solver::init(); @@ -63,9 +65,8 @@ int EulerSolver::init() { } int EulerSolver::run() { - TRACE("EulerSolver::run()"); - for (int s = 0; s < getNumberOutputSteps(); s++) { + for (int s = 1; s <= getNumberOutputSteps(); s++) { BoutReal target = simtime + getOutputTimestep(); bool running = true; @@ -141,7 +142,37 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star Array& result) { load_vars(std::begin(start)); + const bool dump_now = + (dump_at_time >= 0 && std::abs(dump_at_time - curtime) < dt) || dump_at_time < -3; + std::shared_ptr debug_ptr; + if (dump_now) { + debug_ptr = std::make_shared(); + Options& debug = *debug_ptr; + for (auto& f : f3d) { + f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug_ptr); + debug[fmt::format("pre_{:s}", f.name)] = *f.var; + f.var->allocate(); + } + } + run_rhs(curtime); + if (dump_now) { + Options& debug = *debug_ptr; + Mesh* mesh{nullptr}; + for (auto& f : f3d) { + saveParallel(debug, f.name, *f.var); + mesh = f.var->getMesh(); + } + + const std::string outnumber = + dump_at_time < -3 ? fmt::format(".{}", debug_counter++) : ""; + const std::string outname = fmt::format("BOUT.debug{}", outnumber); + bout::OptionsIO::write(outname, debug, mesh); + MPI_Barrier(BoutComm::get()); + for (auto& f : f3d) { + f.F_var->disableTracking(); + } + } save_derivs(std::begin(result)); BOUT_OMP_PERF(parallel for) diff --git a/src/solver/impls/euler/euler.hxx b/src/solver/impls/euler/euler.hxx index 0ee81a3d33..fc9b7f53bb 100644 --- a/src/solver/impls/euler/euler.hxx +++ b/src/solver/impls/euler/euler.hxx @@ -64,6 +64,9 @@ private: /// Take a single step to calculate f1 void take_step(BoutReal curtime, BoutReal dt, Array& start, Array& result); + + BoutReal dump_at_time{-1.}; + int debug_counter{0}; }; #endif // BOUT_KARNIADAKIS_SOLVER_H diff --git a/src/solver/impls/ida/ida.cxx b/src/solver/impls/ida/ida.cxx index e0354e8b57..00e91f17c1 100644 --- a/src/solver/impls/ida/ida.cxx +++ b/src/solver/impls/ida/ida.cxx @@ -33,6 +33,8 @@ #if BOUT_HAS_IDA +#include "../../sundials_nvector_interface.hxx" + #include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" @@ -51,7 +53,6 @@ #include #include -#include #include #include #include @@ -80,6 +81,9 @@ IdaSolver::IdaSolver(Options* opts) correct_start((*options)["correct_start"] .doc("Correct the initial values") .withDefault(true)), + nvector_type((*options)["nvector"] + .doc("N_Vector backend to use: sundials or manyvector") + .withDefault(NVectorType::Sundials)), suncontext(createSUNContext(BoutComm::get())) { has_constraints = true; // This solver has constraints } @@ -99,10 +103,10 @@ IdaSolver::~IdaSolver() { **************************************************************************/ int IdaSolver::init() { - - TRACE("Initialising IDA solver"); + const auto backend = nvector_backend(); Solver::init(); + backend.ensure_manyvector_available(); output.write("Initialising IDA solver\n"); @@ -121,32 +125,21 @@ int IdaSolver::init() { output.write("\t3d fields = {:d}, 2d fields = {:d} neq={:d}, local_N={:d}\n", n3d, n2d, neq, local_N); + output.write("\tUsing {} N_Vector backend\n", backend.backend_name()); // Allocate memory - uvec = callWithSUNContext(N_VNew_Parallel, suncontext, BoutComm::get(), local_N, neq); - if (uvec == nullptr) { - throw BoutException("SUNDIALS memory allocation failed\n"); - } - duvec = N_VClone(uvec); - if (duvec == nullptr) { - throw BoutException("SUNDIALS memory allocation failed\n"); - } - id = N_VClone(uvec); - if (id == nullptr) { - throw BoutException("SUNDIALS memory allocation failed\n"); - } - - // Put the variables into uvec - save_vars(N_VGetArrayPointer(uvec)); + uvec = backend.create_state_vector(local_N, neq); + duvec = backend.clone_vector_like(uvec, "SUNDIALS memory allocation failed"); + id = backend.clone_vector_like(uvec, "SUNDIALS memory allocation failed"); // Get the starting time derivative run_rhs(simtime); // Put the time-derivatives into duvec - save_derivs(N_VGetArrayPointer(duvec)); + backend.copy_deriv_to_vector(duvec); // Set the equation type in id(Differential or Algebraic. This is optional) - set_id(N_VGetArrayPointer(id)); + backend.fill_id_vector(id); // Call IDACreate to initialise idamem = callWithSUNContext(IDACreate, suncontext); @@ -233,13 +226,12 @@ int IdaSolver::init() { **************************************************************************/ int IdaSolver::run() { - TRACE("IDA IdaSolver::run()"); if (!initialised) { throw BoutException("IdaSolver not initialised\n"); } - for (int i = 0; i < getNumberOutputSteps(); i++) { + for (int i = 1; i <= getNumberOutputSteps(); i++) { /// Run the solver for one output timestep simtime = run(simtime + getOutputTimestep()); @@ -261,6 +253,7 @@ int IdaSolver::run() { BoutReal IdaSolver::run(BoutReal tout) { TRACE("Running solver: solver::run({:e})", tout); + const auto backend = nvector_backend(); if (!initialised) { throw BoutException("Running IDA solver without initialisation\n"); @@ -272,7 +265,7 @@ BoutReal IdaSolver::run(BoutReal tout) { const int flag = IDASolve(idamem, tout, &simtime, uvec, duvec, IDA_NORMAL); // Copy variables - load_vars(N_VGetArrayPointer(uvec)); + backend.copy_state_from_vector(uvec); // Call rhs function to get extra variables at this time run_rhs(simtime); @@ -290,55 +283,50 @@ BoutReal IdaSolver::run(BoutReal tout) { * Residual function F(t, u, du) **************************************************************************/ -void IdaSolver::res(BoutReal t, BoutReal* udata, BoutReal* dudata, BoutReal* rdata) { +void IdaSolver::res(BoutReal t, N_Vector u, N_Vector du, N_Vector rr) { TRACE("Running RHS: IdaSolver::res({:e})", t); + const auto backend = nvector_backend(); // Load state from udata - load_vars(udata); + backend.copy_state_from_vector(u); // Call RHS function run_rhs(t); // Save derivatives to rdata (residual) - save_derivs(rdata); - - // If a differential equation, subtract dudata - const auto length = N_VGetLocalLength_Parallel(id); - const BoutReal* idd = N_VGetArrayPointer(id); - for (int i = 0; i < length; i++) { - if (idd[i] > 0.5) { // 1 -> differential, 0 -> algebraic - rdata[i] -= dudata[i]; - } - } + backend.copy_state_to_vector(u); + backend.copy_deriv_to_vector(rr); + backend.subtract_differential_term(rr, du, id); } /************************************************************************** * Preconditioner function **************************************************************************/ -void IdaSolver::pre(BoutReal t, BoutReal cj, BoutReal delta, BoutReal* udata, - BoutReal* rvec, BoutReal* zvec) { +void IdaSolver::pre(BoutReal t, BoutReal cj, BoutReal delta, N_Vector u, N_Vector rvec, + N_Vector zvec) { TRACE("Running preconditioner: IdaSolver::pre({:e})", t); + const auto backend = nvector_backend(); const BoutReal tstart = bout::globals::mpi->MPI_Wtime(); if (!hasPreconditioner()) { // Identity (but should never happen) - const auto length = N_VGetLocalLength_Parallel(id); - std::copy(rvec, rvec + length, zvec); + N_VScale(1.0, rvec, zvec); return; } // Load state from udata (as with res function) - load_vars(udata); + backend.copy_state_from_vector(u); // Load vector to be inverted into F_vars - load_derivs(rvec); + backend.copy_deriv_from_vector(rvec); runPreconditioner(t, cj, delta); // Save the solution from F_vars - save_derivs(zvec); + backend.copy_state_to_vector(u); + backend.copy_deriv_to_vector(zvec); pre_Wtime += bout::globals::mpi->MPI_Wtime() - tstart; pre_ncalls++; @@ -351,14 +339,10 @@ void IdaSolver::pre(BoutReal t, BoutReal cj, BoutReal delta, BoutReal* udata, // NOLINTBEGIN(readability-identifier-length) namespace { int idares(BoutReal t, N_Vector u, N_Vector du, N_Vector rr, void* user_data) { - BoutReal* udata = N_VGetArrayPointer(u); - BoutReal* dudata = N_VGetArrayPointer(du); - BoutReal* rdata = N_VGetArrayPointer(rr); - auto* s = static_cast(user_data); // Calculate residuals - s->res(t, udata, dudata, rdata); + s->res(t, u, du, rr); return 0; } @@ -372,14 +356,10 @@ int ida_bbd_res(sunindextype UNUSED(Nlocal), BoutReal t, N_Vector u, N_Vector du // Preconditioner function int ida_pre(BoutReal t, N_Vector yy, N_Vector UNUSED(yp), N_Vector UNUSED(rr), N_Vector rvec, N_Vector zvec, BoutReal cj, BoutReal delta, void* user_data) { - BoutReal* udata = N_VGetArrayPointer(yy); - BoutReal* rdata = N_VGetArrayPointer(rvec); - BoutReal* zdata = N_VGetArrayPointer(zvec); - auto* s = static_cast(user_data); // Calculate residuals - s->pre(t, cj, delta, udata, rdata, zdata); + s->pre(t, cj, delta, yy, rvec, zvec); return 0; } diff --git a/src/solver/impls/ida/ida.hxx b/src/solver/impls/ida/ida.hxx index 199539f1d6..afce71401b 100644 --- a/src/solver/impls/ida/ida.hxx +++ b/src/solver/impls/ida/ida.hxx @@ -42,6 +42,7 @@ RegisterUnavailableSolver #else +#include "../../sundials_nvector_interface.hxx" #include "bout/bout_types.hxx" #include "bout/sundials_backports.hxx" @@ -64,9 +65,9 @@ public: BoutReal run(BoutReal tout); // These functions used internally (but need to be public) - void res(BoutReal t, BoutReal* udata, BoutReal* dudata, BoutReal* rdata); - void pre(BoutReal t, BoutReal cj, BoutReal delta, BoutReal* udata, BoutReal* rvec, - BoutReal* zvec); + void res(BoutReal t, N_Vector u, N_Vector du, N_Vector rr); + void pre(BoutReal t, BoutReal cj, BoutReal delta, N_Vector u, N_Vector rvec, + N_Vector zvec); private: /// Absolute tolerance @@ -79,6 +80,8 @@ private: bool use_precon; /// Correct the initial values bool correct_start; + /// N_Vector backend to use + NVectorType nvector_type; N_Vector uvec{nullptr}; // Values N_Vector duvec{nullptr}; // Time-derivatives @@ -88,6 +91,10 @@ private: BoutReal pre_Wtime{0.0}; // Time in preconditioner int pre_ncalls{0}; // Number of calls to preconditioner + SundialsNVectorInterface nvector_backend() { + return SundialsNVectorInterface(*this, suncontext, nvector_type); + } + /// SPGMR solver structure SUNLinearSolver sun_solver{nullptr}; /// Context for SUNDIALS memory allocations diff --git a/src/solver/impls/imex-bdf2/imex-bdf2.cxx b/src/solver/impls/imex-bdf2/imex-bdf2.cxx index 853d8eaf12..e30ae4c743 100644 --- a/src/solver/impls/imex-bdf2/imex-bdf2.cxx +++ b/src/solver/impls/imex-bdf2/imex-bdf2.cxx @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include @@ -113,7 +113,7 @@ static PetscErrorCode FormFunctionForDifferencing(void* ctx, Vec x, Vec f) { * * This can be a linearised and simplified form of FormFunction */ -static PetscErrorCode FormFunctionForColoring(SNES UNUSED(snes), Vec x, Vec f, +static PetscErrorCode FormFunctionForColoring(void* UNUSED(snes), Vec x, Vec f, void* ctx) { return static_cast(ctx)->snes_function(x, f, true); } @@ -135,8 +135,6 @@ static PetscErrorCode imexbdf2PCapply(PC pc, Vec x, Vec y) { */ int IMEXBDF2::init() { - TRACE("Initialising IMEX-BDF2 solver"); - Solver::init(); output << "\n\tIMEX-BDF2 time-integration solver\n"; @@ -330,7 +328,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { if (mesh->firstX()) { // Lower X boundary for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int localIndex = ROUND(index(mesh->xstart, y, z)); ASSERT2((localIndex >= 0) && (localIndex < localN)); if (z == 0) { @@ -349,7 +347,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { } else { // On another processor for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int localIndex = ROUND(index(mesh->xstart, y, z)); ASSERT2((localIndex >= 0) && (localIndex < localN)); if (z == 0) { @@ -372,7 +370,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { if (mesh->lastX()) { // Upper X boundary for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int localIndex = ROUND(index(mesh->xend, y, z)); ASSERT2((localIndex >= 0) && (localIndex < localN)); if (z == 0) { @@ -391,7 +389,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { } else { // On another processor for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int localIndex = ROUND(index(mesh->xend, y, z)); ASSERT2((localIndex >= 0) && (localIndex < localN)); if (z == 0) { @@ -558,7 +556,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { } // 3D fields - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int ind = ROUND(index(x, y, z)); @@ -651,9 +649,8 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { // Create data structure for SNESComputeJacobianDefaultColor MatFDColoringCreate(Jmf, iscoloring, &fdcoloring); // Set the function to difference - MatFDColoringSetFunction( - fdcoloring, reinterpret_cast(FormFunctionForColoring), - this); + MatFDColoringSetFunction(fdcoloring, + bout::cast_MatFDColoringFn(FormFunctionForColoring), this); MatFDColoringSetFromOptions(fdcoloring); MatFDColoringSetUp(Jmf, iscoloring, fdcoloring); ISColoringDestroy(&iscoloring); @@ -677,12 +674,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { 0, // Number of nonzeros per row in off-diagonal portion of local submatrix nullptr, &Jmf); -#if PETSC_VERSION_GE(3, 4, 0) SNESSetJacobian(*snesIn, Jmf, Jmf, SNESComputeJacobianDefault, this); -#else - // Before 3.4 - SNESSetJacobian(*snesIn, Jmf, Jmf, SNESDefaultComputeJacobian, this); -#endif MatSetOption(Jmf, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE); } @@ -746,7 +738,6 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { } int IMEXBDF2::run() { - TRACE("IMEXBDF2::run()"); // Multi-step scheme, so first steps are different int order = 1; @@ -760,7 +751,7 @@ int IMEXBDF2::run() { int internalCounter = 0; // Cumulative number of successful internal iterations - for (int s = 0; s < getNumberOutputSteps(); s++) { + for (int s = 1; s <= getNumberOutputSteps(); s++) { BoutReal cumulativeTime = 0.; int counter = 0; // How many iterations in this output step @@ -1425,7 +1416,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { if (mesh->firstX() && !mesh->periodicX) { for (int jx = 0; jx < mesh->xstart; ++jx) { for (int jy = mesh->ystart; jy <= mesh->yend; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(jx, jy, jz, u); ++u; } @@ -1437,7 +1428,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { if (mesh->lastX() && !mesh->periodicX) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; ++jx) { for (int jy = mesh->ystart; jy <= mesh->yend; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(jx, jy, jz, u); ++u; } @@ -1447,7 +1438,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { // Lower Y for (RangeIterator xi = mesh->iterateBndryLowerY(); !xi.isDone(); ++xi) { for (int jy = 0; jy < mesh->ystart; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(*xi, jy, jz, u); ++u; } @@ -1457,7 +1448,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { // Upper Y for (RangeIterator xi = mesh->iterateBndryUpperY(); !xi.isDone(); ++xi) { for (int jy = mesh->yend + 1; jy < mesh->LocalNy; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(*xi, jy, jz, u); ++u; } @@ -1468,7 +1459,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { // Bulk of points for (int jx = mesh->xstart; jx <= mesh->xend; ++jx) { for (int jy = mesh->ystart; jy <= mesh->yend; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(jx, jy, jz, u); ++u; } diff --git a/src/solver/impls/petsc/petsc.cxx b/src/solver/impls/petsc/petsc.cxx index 090087d0de..d123b56508 100644 --- a/src/solver/impls/petsc/petsc.cxx +++ b/src/solver/impls/petsc/petsc.cxx @@ -2,9 +2,9 @@ * Interface to PETSc solver * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -24,61 +24,245 @@ **************************************************************************/ #include "bout/build_defines.hxx" +#include "bout/field3d.hxx" +#include "bout/petsclib.hxx" + +#if BOUT_HAS_PETSC #include "petsc.hxx" -#if BOUT_HAS_PETSC +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PETSC_UNLIMITED +// Introduced in PETSc 3.22 +#define PETSC_UNLIMITED (-3) +#endif -#include +namespace { +// PETSc callback function for matrix-free preconditioner +PetscErrorCode snesPCapply(PC pc, Vec x, Vec y) { + // Get the context + void* ctx = nullptr; + PetscCall(PCShellGetContext(pc, &ctx)); + // Run the preconditioner + PetscFunctionReturn(static_cast(ctx)->pre(x, y)); +} -#include +// PETSc callback function, that evaluates the nonlinear +// function being integrated by TS. +PetscErrorCode solver_rhs(TS UNUSED(ts), BoutReal t, Vec globalin, Vec globalout, + void* f_data) { + PetscFunctionBegin; + auto* s = static_cast(f_data); + s->rhs(t, globalin, globalout, false); + PetscFunctionReturn(0); +} -#include // Cell interpolation -#include -#include +// Form function for use with SUNDIALS. +// This is needed because SNESTSFormFunction is not available +// PETSc error: No method snesfunction for TS of type sundials +PetscErrorCode solver_form_function(void* UNUSED(dummy), Vec U, Vec F, void* f_data) { + PetscFunctionBegin; + auto* s = static_cast(f_data); + s->formFunction(U, F); + PetscFunctionReturn(0); +} +} // namespace + +// Compute IJacobian = dF/dU + a dF/dUdot +// This is a dummy matrix that saves the shift. +// The shift is later used in the matrix-free preconditioner +PetscErrorCode solver_ijacobian(TS, BoutReal, Vec, Vec, PetscReal shift, Mat J, Mat Jpre, + void* ctx) { + auto* solver = static_cast(ctx); + solver->shift = shift; + + PetscCall(MatAssemblyBegin(Jpre, MAT_FINAL_ASSEMBLY)); + PetscCall(MatAssemblyEnd(Jpre, MAT_FINAL_ASSEMBLY)); + if (J != Jpre) { + PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY)); + PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY)); + } + + PetscFunctionReturn(0); +} + +PetscErrorCode solver_ijacobian_color(TS ts, PetscReal t, Vec U, Vec Udot, + PetscReal shift, Mat J, Mat B, void* ctx) { + auto* solver = static_cast(ctx); + solver->shift = shift; + + return TSComputeIJacobianDefaultColor(ts, t, U, Udot, shift, J, B, nullptr); +} + +// This function is called by the TS object every internal timestep +// It is responsible for triggering interpolation and output at +// the output time interval. +PetscErrorCode PetscMonitor(TS ts, PetscInt UNUSED(step), PetscReal t, Vec X, void* ctx) { + PetscFunctionBegin; + + auto* s = static_cast(ctx); + + if (s->diagnose) { + // Print diagnostic information. + // Using the same format at the SNES solver + + SNESConvergedReason reason; + SNESGetConvergedReason(s->snes, &reason); + + PetscReal timestep; + TSGetTimeStep(s->ts, ×tep); + + // SNES failure counter is only reset when TSSolve is called + static PetscInt prev_snes_failures = 0; + PetscInt snes_failures; + TSGetSNESFailures(s->ts, &snes_failures); + snes_failures -= prev_snes_failures; + prev_snes_failures += snes_failures; + + // Get number of iterations + int nl_its; + SNESGetIterationNumber(s->snes, &nl_its); + int lin_its; + SNESGetLinearSolveIterations(s->snes, &lin_its); + + output.print("\r"); // Carriage return for printing to screen + output.write("Time: {}, timestep: {}, nl iter: {}, lin iter: {}, reason: {}", t, + timestep, nl_its, lin_its, static_cast(reason)); + if (snes_failures > 0) { + output.write(", SNES failures: {}", snes_failures); + } + output.write("\n"); + } -extern PetscErrorCode solver_f(TS ts, BoutReal t, Vec globalin, Vec globalout, - void* f_data); + if (t < s->next_output) { + // Not reached output time yet => return + PetscFunctionReturn(0); + } -#if PETSC_VERSION_GE(3, 5, 0) -extern PetscErrorCode solver_rhsjacobian(TS ts, BoutReal t, Vec globalin, Mat J, Mat Jpre, - void* f_data); -extern PetscErrorCode solver_ijacobianfd(TS, PetscReal, Vec, Vec, PetscReal, Mat, Mat, - void*); + PetscReal tfinal; + static int i = 0; + +#if PETSC_VERSION_GE(3, 8, 0) + PetscCall(TSGetMaxTime(ts, &tfinal)); #else -extern PetscErrorCode solver_rhsjacobian(TS ts, BoutReal t, Vec globalin, Mat* J, - Mat* Jpre, MatStructure* str, void* f_data); -extern PetscErrorCode solver_ijacobianfd(TS, PetscReal, Vec, Vec, PetscReal, Mat*, Mat*, - MatStructure*, void*); + PetscCall(TSGetDuration(ts, nullptr, &tfinal)); #endif -extern PetscErrorCode solver_if(TS, BoutReal, Vec, Vec, Vec, void*); + // Duplicate the solution vector X into a work vector + Vec interpolatedX; + PetscCall(VecDuplicate(X, &interpolatedX)); + + // The internal timestepper may have stepped over multiple output times + while (s->next_output <= t && s->next_output <= tfinal) { + BoutReal output_time = t; + if (s->interpolate) { + int ierr = TSInterpolate(ts, s->next_output, interpolatedX); + if (ierr != PETSC_SUCCESS) { + throw BoutException("This PETSc TS does not support interpolation. Use a " + "different method or set solver:interpolate=false"); + } + output_time = s->next_output; + } + + // Place the interpolated values into the global variables + const PetscScalar* x; + PetscCall(VecGetArrayRead(interpolatedX, &x)); + s->load_vars(const_cast(x)); + PetscCall(VecRestoreArrayRead(interpolatedX, &x)); + + if (s->call_monitors(output_time, ++i, s->getNumberOutputSteps()) != 0) { + PetscFunctionReturn(1); + } + + s->next_output = output_time + s->getOutputTimestep(); + } + + // Done with vector, so destroy it + PetscCall(VecDestroy(&interpolatedX)); -/// KSP preconditioner PCShell routines for physics preconditioners -extern PetscErrorCode PhysicsPCApply(PC, Vec x, Vec y); -extern PetscErrorCode PhysicsJacobianApply(Mat J, Vec x, Vec y); -extern PetscErrorCode PhysicsSNESApply(SNES, Vec); + PetscFunctionReturn(0); +} PetscSolver::PetscSolver(Options* opts) - : Solver(opts), + : Solver(opts), interpolate((*options)["interpolate"] + .doc("Interpolate to regular output times?") + .withDefault(true)), diagnose( (*options)["diagnose"].doc("Enable some diagnostic output").withDefault(false)), - adaptive( - (*options)["adaptive"].doc("Use adaptive timestepping").withDefault(false)), - use_precon((*options)["use_precon"] - .doc("Use user-supplied preconditioning function") - .withDefault(false)), - use_jacobian((*options)["use_jacobian"] - .doc("Use user-supplied Jacobian function") - .withDefault(false)), - abstol((*options)["atol"].doc("Absolute tolerance").withDefault(1.0e-12)), - reltol((*options)["rtol"].doc("Relative tolerance").withDefault(1.0e-5)), - adams_moulton((*options)["adams_moulton"] - .doc("Use Adams-Moulton implicit multistep method instead of BDF " - "(requires PETSc to have been built with SUNDIALS)") - .withDefault(false)), + user_precon((*options)["user_precon"] + .doc("Use user-supplied preconditioning function?") + .withDefault(false)), + atol((*options)["atol"].doc("Absolute tolerance").withDefault(1.0e-12)), + rtol((*options)["rtol"].doc("Relative tolerance").withDefault(1.0e-5)), + stol((*options)["stol"] + .doc("Convergence tolerance in terms of the norm of the change in " + "the solution between steps") + .withDefault(1e-8)), + maxnl((*options)["max_nonlinear_iterations"] + .doc("Maximum number of nonlinear iterations per SNES solve") + .withDefault(50)), + maxf((*options)["maxf"] + .doc("Maximum number of function evaluations per SNES solve") + .withDefault(10000)), + maxl((*options)["maxl"].doc("Maximum number of linear iterations").withDefault(20)), + ts_type((*options)["ts_type"].doc("PETSc time integrator type").withDefault("bdf")), + adapt_type((*options)["adapt_type"] + .doc("PETSc TSAdaptType timestep adaptation method") + .withDefault("basic")), + snes_type((*options)["snes_type"] + .doc("PETSc nonlinear solver method to use") + .withDefault("newtonls")), + ksp_type((*options)["ksp_type"] + .doc("Linear solver type. By default let PETSc decide (gmres)") + .withDefault("default")), + pc_type( + (*options)["pc_type"] + .doc("Preconditioner type. By default lets PETSc decide (ilu or bjacobi)") + .withDefault("default")), + pc_hypre_type((*options)["pc_hypre_type"] + .doc("hypre preconditioner type: euclid, pilut, parasails, " + "boomeramg, ams, ads") + .withDefault("pilut")), + line_search_type((*options)["line_search_type"] + .doc("Line search type: basic, bt, l2, cp, nleqerr") + .withDefault("default")), + matrix_free((*options)["matrix_free"] + .doc("Use matrix free Jacobian?") + .withDefault(false)), + matrix_free_operator((*options)["matrix_free_operator"] + .doc("Use matrix free Jacobian-vector operator?") + .withDefault(false)), + lag_jacobian((*options)["lag_jacobian"] + .doc("Re-use the Jacobian this number of SNES iterations") + .withDefault(50)), + use_coloring((*options)["use_coloring"] + .doc("Use matrix coloring to calculate Jacobian?") + .withDefault(true)), + kspsetinitialguessnonzero((*options)["kspsetinitialguessnonzero"] + .doc("Set the initial guess to be non-zero") + .withDefault(false)), start_timestep((*options)["start_timestep"] .doc("Initial internal timestep (defaults to output timestep)") .withDefault(getOutputTimestep())), @@ -87,9 +271,8 @@ PetscSolver::PetscSolver(Options* opts) PetscSolver::~PetscSolver() { VecDestroy(&u); - MatDestroy(&J); + MatDestroy(&Jfd); MatDestroy(&Jmf); - MatFDColoringDestroy(&matfdcoloring); TSDestroy(&ts); } @@ -98,133 +281,70 @@ PetscSolver::~PetscSolver() { **************************************************************************/ int PetscSolver::init() { - PetscErrorCode ierr; - int neq; - MPI_Comm comm = PETSC_COMM_WORLD; - PetscMPIInt rank; - - TRACE("Initialising PETSc-dev solver"); - - PetscFunctionBegin; - PetscLogEventRegister("PetscSolver::init", PETSC_VIEWER_CLASSID, &init_event); - PetscLogEventRegister("loop_vars", PETSC_VIEWER_CLASSID, &loop_event); - PetscLogEventRegister("solver_f", PETSC_VIEWER_CLASSID, &solver_event); Solver::init(); - ierr = PetscLogEventBegin(init_event, 0, 0, 0, 0); - CHKERRQ(ierr); - output.write("Initialising PETSc-dev solver\n"); - ierr = bout::globals::mpi->MPI_Comm_rank(comm, &rank); - CHKERRQ(ierr); - - PetscInt local_N = getLocalN(); // Number of evolving variables on this processor - - /********** Get total problem size **********/ - if (bout::globals::mpi->MPI_Allreduce(&local_N, &neq, 1, MPI_INT, MPI_SUM, - BoutComm::get())) { - output_error.write("\tERROR: MPI_Allreduce failed!\n"); - ierr = PetscLogEventEnd(init_event, 0, 0, 0, 0); - CHKERRQ(ierr); - PetscFunctionReturn(1); + int nlocal = getLocalN(); // Number of evolving variables on this processor + + // Get total problem size + int neq; + if (bout::globals::mpi->MPI_Allreduce(&nlocal, &neq, 1, MPI_INT, MPI_SUM, + BoutComm::get()) + != 0) { + throw BoutException("MPI_Allreduce failed!"); } - ierr = VecCreate(BoutComm::get(), &u); - CHKERRQ(ierr); - ierr = VecSetSizes(u, local_N, PETSC_DECIDE); - CHKERRQ(ierr); - ierr = VecSetFromOptions(u); - CHKERRQ(ierr); + output_info.write("\t3d fields = {:d}, 2d fields = {:d} neq={:d}, local_N={:d}\n", + n3Dvars(), n2Dvars(), neq, nlocal); - ////////// SAVE INITIAL STATE TO PETSc VECTOR /////////// - // Set pointer to data array in vector u. - BoutReal* udata; + // Create a vector to contain the state + PetscCall(VecCreate(BoutComm::get(), &u)); + PetscCall(VecSetSizes(u, nlocal, PETSC_DECIDE)); + PetscCall(VecSetFromOptions(u)); - ierr = VecGetArray(u, &udata); - CHKERRQ(ierr); + // Save initial state to PETSc Vec + BoutReal* udata; // Pointer to data array in vector u. + PetscCall(VecGetArray(u, &udata)); save_vars(udata); - ierr = VecRestoreArray(u, &udata); - CHKERRQ(ierr); - - PetscReal norm; - ierr = VecNorm(u, NORM_1, &norm); - CHKERRQ(ierr); - output_info << "initial |u| = " << norm << "\n"; - - PetscBool J_load; - char load_file[PETSC_MAX_PATH_LEN]; /* jacobian input file name */ - PetscBool J_write = PETSC_FALSE, J_slowfd = PETSC_FALSE; + PetscCall(VecRestoreArray(u, &udata)); // Create timestepper - ierr = TSCreate(BoutComm::get(), &ts); - CHKERRQ(ierr); - ierr = TSSetProblemType(ts, TS_NONLINEAR); - CHKERRQ(ierr); -#if PETSC_HAS_SUNDIALS - ierr = TSSetType(ts, TSSUNDIALS); - CHKERRQ(ierr); -#else - ierr = TSSetType(ts, TSRK); - CHKERRQ(ierr); -#endif - ierr = TSSetApplicationContext(ts, this); - CHKERRQ(ierr); + PetscCall(TSCreate(BoutComm::get(), &ts)); + PetscCall(TSSetProblemType(ts, TS_NONLINEAR)); + PetscCall(TSSetType(ts, ts_type.c_str())); + PetscCall(TSSetApplicationContext(ts, this)); // Set user provided RHSFunction // Need to duplicate the solution vector for the residual Vec rhs_vec; - ierr = VecDuplicate(u, &rhs_vec); - CHKERRQ(ierr); - ierr = TSSetRHSFunction(ts, rhs_vec, solver_f, this); - CHKERRQ(ierr); - ierr = VecDestroy(&rhs_vec); - CHKERRQ(ierr); + PetscCall(VecDuplicate(u, &rhs_vec)); + PetscCall(TSSetRHSFunction(ts, rhs_vec, solver_rhs, this)); // Set up adaptive time-stepping TSAdapt adapt; - ierr = TSGetAdapt(ts, &adapt); - CHKERRQ(ierr); - if (adaptive) { - ierr = TSAdaptSetType(adapt, TSADAPTBASIC); - CHKERRQ(ierr); - } else { - ierr = TSAdaptSetType(adapt, TSADAPTNONE); - CHKERRQ(ierr); - } + PetscCall(TSGetAdapt(ts, &adapt)); + PetscCall(TSAdaptSetType(adapt, adapt_type.c_str())); // Set default absolute/relative tolerances - ierr = TSSetTolerances(ts, abstol, nullptr, reltol, nullptr); - CHKERRQ(ierr); - -#if PETSC_VERSION_LT(3, 5, 0) - ierr = TSRKSetTolerance(ts, reltol); - CHKERRQ(ierr); + // Note: Vector atol and rtol not given + PetscCall(TSSetTolerances(ts, atol, nullptr, rtol, nullptr)); + if (ts_type == TSSUNDIALS) { +#if PETSC_HAVE_SUNDIALS2 + // The PETSc interface to SUNDIALS' CVODE + TSSundialsSetType(ts, SUNDIALS_BDF); + TSSundialsSetTolerance(ts, atol, rtol); +#else + throw BoutException("PETSc was not built with SUNDIALS. Reconfigure and build PETSc " + "with --download-sundials"); #endif - -#if PETSC_HAS_SUNDIALS - // Set Sundials tolerances - ierr = TSSundialsSetTolerance(ts, abstol, reltol); - CHKERRQ(ierr); - - // Select Sundials Adams-Moulton or BDF method - if (adams_moulton) { - output.write("\tUsing Adams-Moulton implicit multistep method\n"); - ierr = TSSundialsSetType(ts, SUNDIALS_ADAMS); - CHKERRQ(ierr); - } else { - output.write("\tUsing BDF method\n"); - ierr = TSSundialsSetType(ts, SUNDIALS_BDF); - CHKERRQ(ierr); } -#endif - // Initial time and timestep - ierr = TSSetTime(ts, simtime); - CHKERRQ(ierr); - ierr = TSSetTimeStep(ts, start_timestep); - CHKERRQ(ierr); + // Initial time of the simulation state + PetscCall(TSSetTime(ts, simtime)); next_output = simtime; + PetscCall(TSSetTimeStep(ts, start_timestep)); + // Total number of steps PetscInt total_steps = mxstep * getNumberOutputSteps(); // Final output time @@ -233,391 +353,270 @@ int PetscSolver::init() { simtime); #if PETSC_VERSION_GE(3, 8, 0) - ierr = TSSetMaxSteps(ts, total_steps); - CHKERRQ(ierr); - ierr = TSSetMaxTime(ts, tfinal); - CHKERRQ(ierr); + PetscCall(TSSetMaxSteps(ts, total_steps)); + PetscCall(TSSetMaxTime(ts, tfinal)); #else - ierr = TSSetDuration(ts, total_steps, tfinal); - CHKERRQ(ierr); + PetscCall(TSSetDuration(ts, total_steps, tfinal)); #endif - // Set the current solution - ierr = TSSetSolution(ts, u); - CHKERRQ(ierr); - - // Create RHSJacobian J - SNES snes, psnes; - KSP ksp, nksp; - PC pc, npc; - PCType pctype; - TSType tstype; - PetscBool pcnone = PETSC_TRUE; - - ierr = TSGetSNES(ts, &snes); - CHKERRQ(ierr); - ierr = TSSetExactFinalTime(ts, TS_EXACTFINALTIME_INTERPOLATE); - CHKERRQ(ierr); - -#if PETSC_VERSION_GE(3, 7, 0) - ierr = PetscOptionsGetBool(nullptr, nullptr, "-interpolate", &interpolate, nullptr); - CHKERRQ(ierr); -#else - ierr = PetscOptionsGetBool(nullptr, "-interpolate", &interpolate, nullptr); - CHKERRQ(ierr); -#endif - - // Check for -output_name to see if user specified a "performance" - // run, if they didn't then use the standard monitor function. TODO: - // use PetscFList -#if PETSC_VERSION_GE(3, 7, 0) - ierr = PetscOptionsGetString(nullptr, nullptr, "-output_name", this->output_name, - sizeof this->output_name, &output_flag); - CHKERRQ(ierr); -#else - ierr = PetscOptionsGetString(nullptr, "-output_name", this->output_name, - sizeof this->output_name, &output_flag); - CHKERRQ(ierr); -#endif + // Allow TS to recover from SNES failures + PetscCall(TSSetMaxSNESFailures(ts, PETSC_UNLIMITED)); - // If the output_name is not specified then use the standard monitor function - if (output_flag != 0U) { - ierr = SNESMonitorSet(snes, PetscSNESMonitor, this, nullptr); - CHKERRQ(ierr); - } else { - ierr = TSMonitorSet(ts, PetscMonitor, this, nullptr); - CHKERRQ(ierr); - } + // Recover from step rejections + PetscCall(TSSetMaxStepRejections(ts, PETSC_UNLIMITED)); - ierr = SNESSetTolerances(snes, abstol, reltol, PETSC_DEFAULT, PETSC_DEFAULT, - PETSC_DEFAULT); - CHKERRQ(ierr); - - // Matrix free Jacobian - - if (use_jacobian and hasJacobian()) { - // Use a user-supplied Jacobian function - ierr = MatCreateShell(comm, local_N, local_N, neq, neq, this, &Jmf); - CHKERRQ(ierr); - ierr = MatShellSetOperation(Jmf, MATOP_MULT, - reinterpret_cast(PhysicsJacobianApply)); - CHKERRQ(ierr); - ierr = TSSetIJacobian(ts, Jmf, Jmf, solver_ijacobian, this); - CHKERRQ(ierr); + // Set the current solution + PetscCall(TSSetSolution(ts, u)); + // Allow TS to step over the final time + // Note: This does not affect intermediate outputs, that + // are always interpolated (in PetscMonitor) + if (interpolate) { + PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_INTERPOLATE)); } else { - // Use finite difference approximation - ierr = MatCreateSNESMF(snes, &Jmf); - CHKERRQ(ierr); - ierr = SNESSetJacobian(snes, Jmf, Jmf, MatMFFDComputeJacobian, this); - CHKERRQ(ierr); + PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP)); } + PetscCall(TSMonitorSet(ts, PetscMonitor, this, nullptr)); - ierr = SNESGetKSP(snes, &ksp); - CHKERRQ(ierr); + if (ts_type != TSSUNDIALS) { + // Get and configure the SNES nonlinear solver + // Note: SUNDIALS does not use PETSc SNES or KSP + // https://petsc.org/release/manual/ts/#using-sundials-from-petsc - ierr = KSPSetTolerances(ksp, reltol, abstol, PETSC_DEFAULT, PETSC_DEFAULT); - CHKERRQ(ierr); + PetscCall(TSGetSNES(ts, &snes)); + PetscCall(SNESSetType(snes, snes_type.c_str())); - ierr = KSPGetPC(ksp, &pc); - CHKERRQ(ierr); + // Line search + if (line_search_type != "default") { + SNESLineSearch linesearch; + SNESGetLineSearch(snes, &linesearch); + SNESLineSearchSetType(linesearch, line_search_type.c_str()); + } - if (use_precon and hasPreconditioner()) { + // Set tolerances + // Note: TS should set SNES convergence tolerances + SNESSetTolerances(snes, + PETSC_DETERMINE, // atol + PETSC_DETERMINE, // rtol + PETSC_DETERMINE, // stol + maxnl, maxf); -#if PETSC_VERSION_GE(3, 5, 0) - ierr = SNESGetNPC(snes, &psnes); - CHKERRQ(ierr); -#else - ierr = SNESGetPC(snes, &psnes); - CHKERRQ(ierr); + // Force SNES to take at least one nonlinear iteration. + // This may prevent the solver from getting stuck in false steady state conditions +#if PETSC_VERSION_GE(3, 8, 0) + SNESSetForceIteration(snes, PETSC_TRUE); #endif - ierr = SNESGetKSP(psnes, &nksp); - CHKERRQ(ierr); - ierr = KSPGetPC(nksp, &npc); - CHKERRQ(ierr); - ierr = SNESSetType(psnes, SNESSHELL); - CHKERRQ(ierr); - ierr = SNESShellSetSolve(psnes, PhysicsSNESApply); - CHKERRQ(ierr); - - // Use a user-supplied preconditioner - // Tell PETSc we're using a "shell" preconditioner - ierr = PCSetType(pc, PCSHELL); - CHKERRQ(ierr); + // Re-use Jacobian + // Note: If the 'Amat' Jacobian is matrix free, SNESComputeJacobian + // always updates its reference 'u' vector every nonlinear iteration + SNESSetLagJacobian(snes, lag_jacobian); + SNESSetLagJacobianPersists(snes, PETSC_TRUE); - // Set routine for applying preconditioner - ierr = PCShellSetApply(pc, PhysicsPCApply); - CHKERRQ(ierr); + SNESSetLagPreconditionerPersists(snes, PETSC_TRUE); + SNESSetLagPreconditioner(snes, 1); // Rebuild when Jacobian is rebuilt - // Set context to this solver object - ierr = PCShellSetContext(pc, this); - CHKERRQ(ierr); + // Get and configure the KSP linear solver + SNESGetKSP(snes, &ksp); - // Set name of preconditioner - ierr = PCShellSetName(pc, "PhysicsPreconditioner"); - CHKERRQ(ierr); - - // Need a callback for IJacobian to get shift 'alpha' - ierr = TSSetIJacobian(ts, Jmf, Jmf, solver_ijacobian, this); - CHKERRQ(ierr); + if (ksp_type != "default") { + KSPSetType(ksp, ksp_type.c_str()); + } - // Use right preconditioner - ierr = KSPSetPCSide(ksp, PC_RIGHT); - CHKERRQ(ierr); + if (kspsetinitialguessnonzero) { + // Set the initial guess to be non-zero + KSPSetInitialGuessNonzero(ksp, PETSC_TRUE); + } - } else { - // Default to no preconditioner - ierr = PCSetType(pc, PCNONE); - CHKERRQ(ierr); - } - ierr = TSSetFromOptions(ts); - CHKERRQ(ierr); // enable PETSc runtime options - - ierr = PCGetType(pc, &pctype); - CHKERRQ(ierr); - ierr = TSGetType(ts, &tstype); - CHKERRQ(ierr); - output.write("\tTS type {:s}, PC type {:s}\n", tstype, pctype); - - ierr = PetscObjectTypeCompare(reinterpret_cast(pc), PCNONE, &pcnone); - CHKERRQ(ierr); - if (pcnone) { - ierr = PetscLogEventEnd(init_event, 0, 0, 0, 0); - CHKERRQ(ierr); - PetscFunctionReturn(0); - } - ierr = PetscObjectTypeCompare(reinterpret_cast(pc), PCSHELL, &pcnone); - CHKERRQ(ierr); - if (pcnone) { - ierr = PetscLogEventEnd(init_event, 0, 0, 0, 0); - CHKERRQ(ierr); - PetscFunctionReturn(0); + KSPSetTolerances(ksp, + PETSC_DEFAULT, // rtol + PETSC_DEFAULT, // abstol + PETSC_DEFAULT, // dtol (divergence tolerance) + maxl); // Maximum number of iterations + + // Set up the Jacobian + // Note: We can have both matrix-free Jacobian-vector operator (Jmf) + // and a finite-difference Jacobian matrix (Jfd) + if (matrix_free or matrix_free_operator) { + /* + PETSc SNES matrix free Jacobian, using a different + operator for differencing. + + See PETSc examples + http://www.mcs.anl.gov/petsc/petsc-current/src/snes/examples/tests/ex7.c.html + and this thread: + http://lists.mcs.anl.gov/pipermail/petsc-users/2014-January/020075.html + + */ + MatCreateSNESMF(snes, &Jmf); + + // Set a function to be called for differencing + // This can be a linearised form of the SNES function + // Note: Unsure if setting this will interfere with TS object + // because the SNES Jacobian depends on the timestep + //MatMFFDSetFunction(Jmf, solver_rhs_differencing, this); + } } - // Create Jacobian matrix to be used by preconditioner - output_info << " Get Jacobian matrix at simtime " << simtime << "\n"; -#if PETSC_VERSION_GE(3, 7, 0) - ierr = PetscOptionsGetString(nullptr, nullptr, "-J_load", load_file, - PETSC_MAX_PATH_LEN - 1, &J_load); - CHKERRQ(ierr); -#else - ierr = PetscOptionsGetString(nullptr, "-J_load", load_file, PETSC_MAX_PATH_LEN - 1, - &J_load); - CHKERRQ(ierr); -#endif - if (J_load) { - PetscViewer fd; - output_info << " Load Jmat ...local_N " << local_N << " neq " << neq << "\n"; - ierr = PetscViewerBinaryOpen(comm, load_file, FILE_MODE_READ, &fd); - CHKERRQ(ierr); - ierr = MatCreate(PETSC_COMM_WORLD, &J); - CHKERRQ(ierr); - //ierr = MatSetType(J, MATBAIJ);CHKERRQ(ierr); - ierr = MatSetSizes(J, local_N, local_N, PETSC_DECIDE, PETSC_DECIDE); - CHKERRQ(ierr); - ierr = MatSetFromOptions(J); - CHKERRQ(ierr); - ierr = MatLoad(J, fd); - CHKERRQ(ierr); - ierr = PetscViewerDestroy(&fd); - CHKERRQ(ierr); - } else { // create Jacobian matrix - - /* number of degrees (variables) at each grid point */ - PetscInt dof = n3Dvars(); - - // Maximum allowable size of stencil in x is the number of guard cells - PetscInt stencil_width_estimate = options->operator[]("stencil_width_estimate") - .withDefault(bout::globals::mesh->xstart); - // This is the stencil in each direction (*2) along each dimension - // (*3), plus the point itself. Not sure if this is correct - // though, on several levels: - // 1. Ignores corner points used in e.g. brackets - // 2. Could have different stencil widths in each dimension - // 3. FFTs couple every single point together - PetscInt cols = stencil_width_estimate * 2 * 3 + 1; - PetscInt prealloc; // = cols*dof; - - ierr = MatCreate(comm, &J); - CHKERRQ(ierr); - ierr = MatSetType(J, MATBAIJ); - CHKERRQ(ierr); - ierr = MatSetSizes(J, local_N, local_N, neq, neq); - CHKERRQ(ierr); - ierr = MatSetFromOptions(J); - CHKERRQ(ierr); - - // Get nonzero pattern of J - color_none !!! - prealloc = cols * dof * dof; - ierr = MatSeqAIJSetPreallocation(J, prealloc, nullptr); - CHKERRQ(ierr); - ierr = MatMPIAIJSetPreallocation(J, prealloc, nullptr, prealloc, nullptr); - CHKERRQ(ierr); - - // why nonzeros=295900, allocated nonzeros=2816000/12800000 (*dof*dof), number of mallocs used during MatSetValues calls =256? - prealloc = cols; - ierr = MatSeqBAIJSetPreallocation(J, dof, prealloc, nullptr); - CHKERRQ(ierr); - ierr = MatMPIBAIJSetPreallocation(J, dof, prealloc, nullptr, prealloc, nullptr); - CHKERRQ(ierr); -#if PETSC_VERSION_GE(3, 7, 0) - ierr = PetscOptionsHasName(nullptr, nullptr, "-J_slowfd", &J_slowfd); - CHKERRQ(ierr); -#else - ierr = PetscOptionsHasName(nullptr, "-J_slowfd", &J_slowfd); - CHKERRQ(ierr); -#endif - if (J_slowfd != 0U) { // create Jacobian matrix by slow fd - ierr = SNESSetJacobian(snes, J, J, SNESComputeJacobianDefault, nullptr); - CHKERRQ(ierr); - output_info << "SNESComputeJacobian J by slow fd...\n"; - -#if PETSC_VERSION_GE(3, 5, 0) - ierr = TSComputeRHSJacobian(ts, simtime, u, J, J); - CHKERRQ(ierr); -#else - MatStructure flg; - ierr = TSComputeRHSJacobian(ts, simtime, u, &J, &J, &flg); - CHKERRQ(ierr); + // Configure preconditioner + PC pc; + if (ts_type == TSSUNDIALS) { +#if PETSC_HAVE_SUNDIALS2 + TSSundialsGetPC(ts, &pc); #endif - output_info << "compute J by slow fd is done.\n"; - } else { // get sparse pattern of the Jacobian - throw BoutException("Sorry, unimplemented way of setting PETSc preconditioner. " - "Either set a preconditioner function with 'setPrecon' " - "in your code, or load a matrix with '-Jload' on the " - "command line, or calculate with finite differences " - "with '-J_slowfd' (on command line)"); - } + } else { + // Get PC context from KSP + KSPGetPC(ksp, &pc); } + if (matrix_free) { + // Matrix-free preconditioner - // Create coloring context of J to be used during time stepping + if (user_precon) { + output_info.write("\tUsing user-supplied preconditioner\n"); + if (!hasPreconditioner()) { + throw BoutException("Model does not define a preconditioner"); + } + // Set a Shell (matrix-free) preconditioner type + PCSetType(pc, PCSHELL); - output_info << " Create coloring ...\n"; + // Specify the preconditioner function + PCShellSetApply(pc, snesPCapply); - ISColoring iscoloring; -#if PETSC_VERSION_GE(3, 5, 0) - MatColoring coloring; - MatColoringCreate(Jmf, &coloring); - MatColoringSetType(coloring, MATCOLORINGSL); - MatColoringSetFromOptions(coloring); - // Calculate index sets - MatColoringApply(coloring, &iscoloring); - MatColoringDestroy(&coloring); -#else - ierr = MatGetColoring(J, MATCOLORINGSL, &iscoloring); - CHKERRQ(ierr); -#endif - ierr = MatFDColoringCreate(J, iscoloring, &matfdcoloring); - CHKERRQ(ierr); - - ierr = MatFDColoringSetFromOptions(matfdcoloring); - CHKERRQ(ierr); - ierr = ISColoringDestroy(&iscoloring); - CHKERRQ(ierr); - ierr = MatFDColoringSetFunction(matfdcoloring, - reinterpret_cast(solver_f), this); - CHKERRQ(ierr); - ierr = SNESSetJacobian(snes, J, J, SNESComputeJacobianDefaultColor, matfdcoloring); - CHKERRQ(ierr); - - // Write J in binary for study - see ~petsc/src/mat/examples/tests/ex124.c -#if PETSC_VERSION_GE(3, 7, 0) - ierr = PetscOptionsHasName(nullptr, nullptr, "-J_write", &J_write); - CHKERRQ(ierr); -#else - ierr = PetscOptionsHasName(nullptr, "-J_write", &J_write); - CHKERRQ(ierr); -#endif - if (J_write != 0U) { - PetscViewer viewer = nullptr; - output_info.write("\n[{:d}] Test TSComputeRHSJacobian() ...\n", rank); -#if PETSC_VERSION_GE(3, 5, 0) - ierr = TSComputeRHSJacobian(ts, simtime, u, J, J); - CHKERRQ(ierr); + // Context used to supply object pointer + PCShellSetContext(pc, this); + + // Set name of preconditioner + PetscCall(PCShellSetName(pc, "PhysicsPreconditioner")); + } else { + // Can't use preconditioner because no Jacobian matrix available + PCSetType(pc, PCNONE); + } + + // Callback to get the shift parameter + // Use matrix free for both operator and preconditioner + TSSetIJacobian(ts, Jmf, Jmf, solver_ijacobian, this); + + } else { + // Set PC type from input + if (pc_type != "default") { + PCSetType(pc, pc_type.c_str()); + + if (pc_type == "hypre") { +#if PETSC_HAVE_HYPRE + // Set the type of hypre preconditioner + PCHYPRESetType(pc, pc_hypre_type.c_str()); #else - MatStructure J_structure; - ierr = TSComputeRHSJacobian(ts, simtime, u, &J, &J, &J_structure); - CHKERRQ(ierr); + throw BoutException("PETSc was not configured with Hypre."); #endif + } + } - output.write("[{:d}] TSComputeRHSJacobian is done\n", rank); - - if (J_slowfd != 0U) { - output_info.write("[{:d}] writing J in binary to data/Jrhs_dense.dat...\n", rank); - ierr = PetscViewerBinaryOpen(comm, "data/Jrhs_dense.dat", FILE_MODE_WRITE, &viewer); - CHKERRQ(ierr); + // Calculate a Jacobian matrix using finite differences. The finite + // difference Jacobian (Jfd) may be used for both operator and + // preconditioner or, if matrix_free_operator, in only the + // preconditioner. + + if (use_coloring) { + Field3D index = globalIndex(0); + PetscCall(petsc_preconditioner.createJacobianPattern( + index, *options, nlocal, n2Dvars(), n3Dvars(), BoutComm::get())); + output_progress.write("Creating Jacobian coloring\n"); + updateColoring(); } else { - output_info.write("[{:d}] writing J in binary to data/Jrhs_sparse.dat...\n", rank); - ierr = - PetscViewerBinaryOpen(comm, "data/Jrhs_sparse.dat", FILE_MODE_WRITE, &viewer); - CHKERRQ(ierr); + // Brute force calculation + // There is usually no reason to use this, except as a check of + // the coloring calculation. + + MatCreateAIJ( + BoutComm::get(), nlocal, nlocal, // Local sizes + PETSC_DETERMINE, PETSC_DETERMINE, // Global sizes + 3, // Number of nonzero entries in diagonal portion of local submatrix + nullptr, + 0, // Number of nonzeros per row in off-diagonal portion of local submatrix + nullptr, &Jfd); + + if (matrix_free_operator) { + SNESSetJacobian(snes, Jmf, Jfd, SNESComputeJacobianDefault, this); + } else { + SNESSetJacobian(snes, Jfd, Jfd, SNESComputeJacobianDefault, this); + } + + MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE); } - ierr = MatView(J, viewer); - CHKERRQ(ierr); - ierr = PetscViewerDestroy(&viewer); - CHKERRQ(ierr); } - ierr = PetscLogEventEnd(init_event, 0, 0, 0, 0); - CHKERRQ(ierr); - - PetscFunctionReturn(0); + // Get runtime options + lib.setOptionsFromInputFile(ts); + + { + // Some reporting + TSType tstype; + TSGetType(ts, &tstype); + output_info.write("TS Type : {}\n", tstype); + TSAdaptType adapttype; + TSAdaptGetType(adapt, &adapttype); + output_info.write("TS Adapt Type : {}\n", adapttype); + if (ts_type != TSSUNDIALS) { + SNESType snestype; + SNESGetType(snes, &snestype); + output_info.write("SNES Type : {}\n", snestype); + KSPType ksptype; + KSPGetType(ksp, &ksptype); + if (ksptype) { + output_info.write("KSP Type : {}\n", ksptype); + } + } + PCType pctype; + PCGetType(pc, &pctype); + if (pctype) { + output_info.write("PC Type : {}\n", pctype); + } + } + return 0; } -/************************************************************************** - * Run - Advance time - **************************************************************************/ - +// Starts the time integrator PetscErrorCode PetscSolver::run() { - // Set when the next call to monitor is desired next_output = simtime + getOutputTimestep(); PetscFunctionBegin; - if (this->output_flag) { - prev_linear_its = 0; - bout_snes_time = bout::globals::mpi->MPI_Wtime(); - } - + // Run the PETSc time integrator + // The PetscMonitor function is responsible for regular outputs CHKERRQ(TSSolve(ts, u)); - // Gawd, everything is a hack - if ((this->output_flag != 0U) and (BoutComm::rank() == 0)) { - Output petsc_info(output_name); - // Don't write to stdout - petsc_info.disable(); - petsc_info.write("SNES Iteration, KSP Iterations, Wall Time, Norm\n"); - for (const auto& info : snes_list) { - petsc_info.write("{:d}, {:d}, {:e}, {:e}\n", info.it, info.linear_its, info.time, - info.norm); - } - } - PetscFunctionReturn(0); } -/************************************************************************** - * RHS function - **************************************************************************/ - -PetscErrorCode PetscSolver::rhs(TS UNUSED(ts), BoutReal t, Vec udata, Vec dudata) { +// Evaluate the user-supplied RHS function +// This method is called from the static PETSc callback functions +PetscErrorCode PetscSolver::rhs(BoutReal t, Vec udata, Vec dudata, bool linear) { TRACE("Running RHS: PetscSolver::rhs({:e})", t); - const BoutReal* udata_array; - BoutReal* dudata_array; + simtime = t; PetscFunctionBegin; // Load state from PETSc + const BoutReal* udata_array; VecGetArrayRead(udata, &udata_array); load_vars(const_cast(udata_array)); VecRestoreArrayRead(udata, &udata_array); - // Call RHS function - run_rhs(t); + try { + // Call RHS function + run_rhs(t, linear); + } catch (BoutException& e) { + // Simulation might fail, e.g. negative densities + // if timestep too large + output_error.write("BoutException thrown: {}\n", e.what()); + // There is no way to recover and synchronise MPI ranks + // unless they all threw an exception at the same point. + BoutComm::abort(1); + } // Save derivatives to PETSc + BoutReal* dudata_array; VecGetArray(dudata, &dudata_array); save_derivs(dudata_array); VecRestoreArray(dudata, &dudata_array); @@ -625,18 +624,19 @@ PetscErrorCode PetscSolver::rhs(TS UNUSED(ts), BoutReal t, Vec udata, Vec dudata PetscFunctionReturn(0); } -/************************************************************************** - * Preconditioner function - **************************************************************************/ +PetscErrorCode PetscSolver::formFunction(Vec U, Vec F) { + PetscFunctionBegin; + PetscCall(rhs(simtime, U, F, true)); // Can be linearised for coloring -PetscErrorCode PetscSolver::pre(PC UNUSED(pc), Vec x, Vec y) { - TRACE("PetscSolver::pre()"); + // shift * U - dU/dt + PetscCall(VecAXPBY(F, shift, -1.0, U)); + PetscFunctionReturn(0); +} - BoutReal* data; +// Matrix-free preconditioner function +PetscErrorCode PetscSolver::pre(Vec x, Vec y) { - if (diagnose) { - output << "Preconditioning" << endl; - } + BoutReal* data; // Load state VecGetArray(state, &data); @@ -656,355 +656,50 @@ PetscErrorCode PetscSolver::pre(PC UNUSED(pc), Vec x, Vec y) { save_derivs(data); VecRestoreArray(y, &data); - // Petsc's definition of Jacobian differs by a factor from Sundials' - PetscErrorCode ierr = VecScale(y, shift); - CHKERRQ(ierr); - - return 0; -} - -/************************************************************************** - * User-supplied Jacobian function J(state) * x = y - **************************************************************************/ - -PetscErrorCode PetscSolver::jac(Vec x, Vec y) { - TRACE("PetscSolver::jac()"); - - BoutReal* data; - - if (diagnose) { - output << "Jacobian evaluation\n"; - } - - // Load state - VecGetArray(state, &data); - load_vars(data); - VecRestoreArray(state, &data); - - // Load vector to be operated on into F_vars - VecGetArray(x, &data); - load_derivs(data); - VecRestoreArray(x, &data); - - // Call the Jacobian function - runJacobian(ts_time); - - // Save the solution from time derivatives - VecGetArray(y, &data); - save_derivs(data); - VecRestoreArray(y, &data); - - // y = a * x - y - PetscErrorCode ierr = VecAXPBY(y, shift, -1.0, x); - CHKERRQ(ierr); + // Petsc's definition of Jacobian differs by a factor from SUNDIALS' + // PETSc solves (scale + J)^-1 + // SUNDIALS solves (I + gamma J)^-1 + PetscCall(VecScale(y, shift)); return 0; } -/************************************************************************** - * Static functions which can be used for PETSc callbacks - **************************************************************************/ - -PetscErrorCode solver_f(TS ts, BoutReal t, Vec globalin, Vec globalout, void* f_data) { - PetscFunctionBegin; - auto* s = static_cast(f_data); - PetscLogEventBegin(s->solver_event, 0, 0, 0, 0); - s->rhs(ts, t, globalin, globalout); - PetscLogEventEnd(s->solver_event, 0, 0, 0, 0); - PetscFunctionReturn(0); -} - -/* - FormIFunction = Udot - RHSFunction -*/ -PetscErrorCode solver_if(TS ts, BoutReal t, Vec globalin, Vec globalindot, Vec globalout, - void* f_data) { - PetscErrorCode ierr; - - PetscFunctionBegin; - ierr = solver_f(ts, t, globalin, globalout, f_data); - CHKERRQ(ierr); - - ierr = VecAYPX(globalout, -1.0, globalindot); - CHKERRQ(ierr); // globalout = globalindot + (-1)globalout - PetscFunctionReturn(0); -} - -#if PETSC_VERSION_GE(3, 5, 0) -PetscErrorCode solver_rhsjacobian(TS UNUSED(ts), BoutReal UNUSED(t), Vec UNUSED(globalin), - Mat J, Mat Jpre, void* UNUSED(f_data)) { - PetscErrorCode ierr; - - PetscFunctionBegin; - ierr = MatAssemblyBegin(Jpre, MAT_FINAL_ASSEMBLY); - CHKERRQ(ierr); - ierr = MatAssemblyEnd(Jpre, MAT_FINAL_ASSEMBLY); - CHKERRQ(ierr); - if (J != Jpre) { - ierr = MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY); - CHKERRQ(ierr); - ierr = MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY); - CHKERRQ(ierr); - } - PetscFunctionReturn(0); -} -#else -PetscErrorCode solver_rhsjacobian([[maybe_unused]] TS ts, [[maybe_unused]] BoutReal t, - [[maybe_unused]] Vec globalin, Mat* J, Mat* Jpre, - [[maybe_unused]] MatStructure* str, - [[maybe_unused]] void* f_data) { - PetscErrorCode ierr; - - PetscFunctionBegin; - ierr = MatAssemblyBegin(*Jpre, MAT_FINAL_ASSEMBLY); - CHKERRQ(ierr); - ierr = MatAssemblyEnd(*Jpre, MAT_FINAL_ASSEMBLY); - CHKERRQ(ierr); - if (*J != *Jpre) { - ierr = MatAssemblyBegin(*J, MAT_FINAL_ASSEMBLY); - CHKERRQ(ierr); - ierr = MatAssemblyEnd(*J, MAT_FINAL_ASSEMBLY); - CHKERRQ(ierr); - } - PetscFunctionReturn(0); -} -#endif - -/* - solver_ijacobian - Compute IJacobian = dF/dU + a dF/dUdot - a dummy matrix used for pc=none -*/ -#if PETSC_VERSION_GE(3, 5, 0) -PetscErrorCode solver_ijacobian(TS ts, BoutReal t, Vec globalin, Vec UNUSED(globalindot), - PetscReal a, Mat J, Mat Jpre, void* f_data) { - PetscErrorCode ierr; - - PetscFunctionBegin; - ierr = solver_rhsjacobian(ts, t, globalin, J, Jpre, f_data); - CHKERRQ(ierr); - - ////// Save data for preconditioner - auto* solver = static_cast(f_data); - - if (solver->diagnose) { - output << "Saving state, t = " << t << ", a = " << a << endl; - } - - solver->shift = a; // Save the shift 'a' - solver->state = globalin; // Save system state - solver->ts_time = t; - - PetscFunctionReturn(0); -} -#else -PetscErrorCode solver_ijacobian(TS ts, BoutReal t, Vec globalin, - [[maybe_unused]] Vec globalindot, - [[maybe_unused]] PetscReal a, Mat* J, Mat* Jpre, - MatStructure* str, void* f_data) { - PetscErrorCode ierr; - - PetscFunctionBegin; - ierr = solver_rhsjacobian(ts, t, globalin, J, Jpre, str, (void*)f_data); - CHKERRQ(ierr); +void PetscSolver::updateColoring() { + Mat jac = petsc_preconditioner.jacobian(); - ////// Save data for preconditioner - PetscSolver* solver = (PetscSolver*)f_data; - - if (solver->diagnose) { - output << "Saving state, t = " << t << ", a = " << a << endl; + if (ts_type != TSSUNDIALS) { + // Use the SNES function that is defined by the TS method + // SNESTSFormFunction is defined in PETSc ts.c + // The ctx pointer should be the TS object + BOUT_DO_PETSC(petsc_preconditioner.updateColoring(SNESTSFormFunction, ts)); + } else { + // SNESTSFormFunction is not available for SUNDIALS. + // This solver_form_function needs to know the shift + // (SUNDIALS' gamma) that we capture in solver_ijacobian_color. + BOUT_DO_PETSC(petsc_preconditioner.updateColoring(solver_form_function, this)); } - solver->shift = a; // Save the shift 'a' - solver->state = globalin; // Save system state - solver->ts_time = t; - - PetscFunctionReturn(0); -} -#endif - -/* - solver_ijacobianfd - Compute IJacobian = dF/dU + a dF/dUdot using finite deference - not implemented yet -*/ -#if PETSC_VERSION_GE(3, 5, 0) -PetscErrorCode solver_ijacobianfd(TS ts, BoutReal t, Vec globalin, - Vec UNUSED(globalindot), PetscReal UNUSED(a), Mat J, - Mat Jpre, void* f_data) { - PetscErrorCode ierr; - - PetscFunctionBegin; - ierr = solver_rhsjacobian(ts, t, globalin, J, Jpre, f_data); - CHKERRQ(ierr); - //*Jpre + a - PetscFunctionReturn(0); -} -#else -PetscErrorCode solver_ijacobianfd(TS ts, BoutReal t, Vec globalin, Vec globalindot, - PetscReal a, Mat* J, Mat* Jpre, MatStructure* str, - void* f_data) { - PetscErrorCode ierr; - - PetscFunctionBegin; - ierr = solver_rhsjacobian(ts, t, globalin, J, Jpre, str, (void*)f_data); - CHKERRQ(ierr); - //*Jpre + a - PetscFunctionReturn(0); -} -#endif -//----------------------------------------- - -PetscErrorCode PhysicsSNESApply(SNES snes, Vec x) { - PetscErrorCode ierr; - Vec F, Fout; - PetscReal fnorm = 0., foutnorm = 0., dot = 0.; - KSP ksp; - PC pc; - Mat A, B; - - PetscFunctionBegin; - ierr = SNESGetJacobian(snes, &A, &B, nullptr, nullptr); - CHKERRQ(ierr); -#if PETSC_VERSION_GE(3, 5, 0) - ierr = SNESComputeJacobian(snes, x, A, B); - CHKERRQ(ierr); -#else - MatStructure diff = DIFFERENT_NONZERO_PATTERN; - ierr = SNESComputeJacobian(snes, x, &A, &B, &diff); - CHKERRQ(ierr); + // Replace the CTX pointer in SNES Jacobian / TS Jacobian callback + if (ts_type == TSSUNDIALS) { +#if PETSC_HAVE_SUNDIALS2 + // The SUNDIALS interface calls TSGetIJacobian + // https://www.mcs.anl.gov/petsc/petsc-3.14/src/ts/impls/implicit/sundials/sundials.c + // This sets the "TSMatFDColoring" property on the Jacobian, that is used in TSComputeIJacobianDefaultColor + PetscObjectCompose((PetscObject)jac, "TSMatFDColoring", + (PetscObject)petsc_preconditioner.coloring()); + // Call a wrapper function that stores the shift in the PetscSolver + TSSetIJacobian(ts, jac, jac, solver_ijacobian_color, this); #endif - ierr = SNESGetKSP(snes, &ksp); - CHKERRQ(ierr); - ierr = KSPGetPC(ksp, &pc); - CHKERRQ(ierr); - ierr = SNESGetFunction(snes, &F, nullptr, nullptr); - CHKERRQ(ierr); - ierr = SNESComputeFunction(snes, x, F); - CHKERRQ(ierr); - ierr = SNESGetSolutionUpdate(snes, &Fout); - CHKERRQ(ierr); - - ierr = PCApply(pc, F, Fout); - CHKERRQ(ierr); - ierr = VecNorm(Fout, NORM_2, &foutnorm); - CHKERRQ(ierr); - ierr = VecAXPY(x, -1., Fout); - CHKERRQ(ierr); - ierr = SNESComputeFunction(snes, x, F); - CHKERRQ(ierr); - ierr = VecNorm(F, NORM_2, &fnorm); - CHKERRQ(ierr); - ierr = VecDot(F, Fout, &dot); - CHKERRQ(ierr); - output_info << " (Debug) function norm: " << fnorm << ", P(f) norm " << foutnorm - << ", F \\cdot Fout " << dot << " "; -#if PETSC_VERSION_GE(3, 5, 0) - Vec func; - ierr = SNESGetFunction(snes, &func, nullptr, nullptr); - CHKERRQ(ierr); - ierr = VecNorm(func, NORM_2, &fnorm); - CHKERRQ(ierr); -#else - ierr = SNESSetFunctionNorm(snes, fnorm); - CHKERRQ(ierr); -#endif - ierr = SNESMonitor(snes, 0, fnorm); - CHKERRQ(ierr); - - PetscFunctionReturn(0); -} - -PetscErrorCode PhysicsPCApply(PC pc, Vec x, Vec y) { - int ierr; - - // Get the context - PetscSolver* s; - ierr = PCShellGetContext(pc, reinterpret_cast(&s)); - CHKERRQ(ierr); - - PetscFunctionReturn(s->pre(pc, x, y)); -} - -PetscErrorCode PhysicsJacobianApply(Mat J, Vec x, Vec y) { - // Get the context - PetscSolver* s; - int ierr = MatShellGetContext(J, reinterpret_cast(&s)); - CHKERRQ(ierr); - PetscFunctionReturn(s->jac(x, y)); -} - -PetscErrorCode PetscMonitor(TS ts, PetscInt UNUSED(step), PetscReal t, Vec X, void* ctx) { - PetscErrorCode ierr; - auto* s = static_cast(ctx); - PetscReal tfinal, dt; - Vec interpolatedX; - const PetscScalar* x; - static int i = 0; - - PetscFunctionBegin; - ierr = TSGetTimeStep(ts, &dt); - CHKERRQ(ierr); - -#if PETSC_VERSION_GE(3, 8, 0) - ierr = TSGetMaxTime(ts, &tfinal); - CHKERRQ(ierr); -#else - ierr = TSGetDuration(ts, nullptr, &tfinal); - CHKERRQ(ierr); -#endif - - /* Duplicate the solution vector X into a work vector */ - ierr = VecDuplicate(X, &interpolatedX); - CHKERRQ(ierr); - while (s->next_output <= t && s->next_output <= tfinal) { - if (s->interpolate) { - ierr = TSInterpolate(ts, s->next_output, interpolatedX); - CHKERRQ(ierr); - } - - /* Place the interpolated values into the global variables */ - ierr = VecGetArrayRead(interpolatedX, &x); - CHKERRQ(ierr); - s->load_vars(const_cast(x)); - ierr = VecRestoreArrayRead(interpolatedX, &x); - CHKERRQ(ierr); - - if (s->call_monitors(simtime, i++, s->getNumberOutputSteps())) { - PetscFunctionReturn(1); + } else { + if (matrix_free_operator) { + // Use matrix-free calculation for operator, finite difference for preconditioner + SNESSetJacobian(snes, Jmf, jac, SNESComputeJacobianDefaultColor, + petsc_preconditioner.coloring()); + } else { + SNESSetJacobian(snes, jac, jac, SNESComputeJacobianDefaultColor, + petsc_preconditioner.coloring()); } - - s->next_output += s->getOutputTimestep(); - simtime = s->next_output; } - - /* Done with vector, so destroy it */ - ierr = VecDestroy(&interpolatedX); - CHKERRQ(ierr); - - PetscFunctionReturn(0); -} - -PetscErrorCode PetscSNESMonitor(SNES snes, PetscInt its, PetscReal norm, void* ctx) { - PetscErrorCode ierr; - PetscInt linear_its = 0; - BoutReal tmp = .0; - snes_info row; - auto* s = static_cast(ctx); - - PetscFunctionBegin; - - if (!its) { - s->prev_linear_its = 0; - } - ierr = SNESGetLinearSolveIterations(snes, &linear_its); - CHKERRQ(ierr); - tmp = bout::globals::mpi->MPI_Wtime(); - - row.it = its; - s->prev_linear_its = row.linear_its = linear_its - s->prev_linear_its; - row.time = tmp - s->bout_snes_time; - row.norm = norm; - - s->snes_list.push_back(row); - - PetscFunctionReturn(0); } #endif diff --git a/src/solver/impls/petsc/petsc.hxx b/src/solver/impls/petsc/petsc.hxx index be841e8da0..13f74dd4ba 100644 --- a/src/solver/impls/petsc/petsc.hxx +++ b/src/solver/impls/petsc/petsc.hxx @@ -3,9 +3,12 @@ /// NOTE: This class needs tidying, generalising to use FieldData interface /************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Interface to PETSc solver * - * Contact: Ben Dudson, bd512@york.ac.uk + ************************************************************************** + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -43,11 +46,16 @@ class PetscSolver; #include #include +#include #include #include #include #include +#include +#include +#include +#include #include @@ -55,30 +63,6 @@ namespace { RegisterSolver registersolverpetsc("petsc"); } -extern BoutReal simtime; - -/// Monitor function called on every internal timestep -extern PetscErrorCode PetscMonitor(TS, PetscInt, PetscReal, Vec, void* ctx); -/// Monitor function for SNES -extern PetscErrorCode PetscSNESMonitor(SNES, PetscInt, PetscReal, void* ctx); - -/// Compute IJacobian = dF/dU + a dF/dUdot - a dummy matrix used for pc=none -#if PETSC_VERSION_GE(3, 5, 0) -extern PetscErrorCode solver_ijacobian(TS, PetscReal, Vec, Vec, PetscReal, Mat, Mat, - void*); -#else -extern PetscErrorCode solver_ijacobian(TS, PetscReal, Vec, Vec, PetscReal, Mat*, Mat*, - MatStructure*, void*); -#endif - -/// Data for SNES -struct snes_info { - PetscInt it; - PetscInt linear_its; - PetscReal time; - PetscReal norm; -}; - class PetscSolver : public Solver { public: PetscSolver(Options* opts = nullptr); @@ -90,24 +74,22 @@ public: // These functions used internally (but need to be public) /// Wrapper for the RHS function - PetscErrorCode rhs(TS ts, PetscReal t, Vec globalin, Vec globalout); + PetscErrorCode rhs(BoutReal t, Vec udata, Vec dudata, bool linear); + /// Residual calculation. + PetscErrorCode formFunction(Vec U, Vec F); /// Wrapper for the preconditioner - PetscErrorCode pre(PC pc, Vec x, Vec y); - /// Wrapper for the Jacobian function - PetscErrorCode jac(Vec x, Vec y); + PetscErrorCode pre(Vec x, Vec y); // Call back functions that need to access internal state friend PetscErrorCode PetscMonitor(TS, PetscInt, PetscReal, Vec, void* ctx); - friend PetscErrorCode PetscSNESMonitor(SNES, PetscInt, PetscReal, void* ctx); -#if PETSC_VERSION_GE(3, 5, 0) - friend PetscErrorCode solver_ijacobian(TS, PetscReal, Vec, Vec, PetscReal, Mat, Mat, - void*); -#else - friend PetscErrorCode solver_ijacobian(TS, PetscReal, Vec, Vec, PetscReal, Mat*, Mat*, - MatStructure*, void*); -#endif - PetscLogEvent solver_event, loop_event, init_event; + friend PetscErrorCode solver_ijacobian(TS, BoutReal, Vec, Vec, PetscReal shift, Mat J, + Mat Jpre, void* ctx); + + // Wrapper around TSComputeIJacobianDefaultColor that saves the shift + // This is used to compute the Jacobian using coloring with SUNDIALS TS method. + friend PetscErrorCode solver_ijacobian_color(TS ts, PetscReal t, Vec U, Vec Udot, + PetscReal shift, Mat J, Mat B, void* ctx); private: BoutReal shift; ///< Shift (alpha) parameter from TS @@ -116,28 +98,46 @@ private: PetscLib lib; ///< Handles initialising, finalising PETSc - Vec u{nullptr}; ///< PETSc solution vector - TS ts{nullptr}; ///< PETSc timestepper object - Mat J{nullptr}; ///< RHS Jacobian - Mat Jmf{nullptr}; - MatFDColoring matfdcoloring{nullptr}; - - bool diagnose; ///< If true, print some information about current stage + Vec u{nullptr}; ///< PETSc solution vector + TS ts{nullptr}; ///< PETSc timestepper object + SNES snes{nullptr}; ///< PETSc nonlinear solver object + KSP ksp{nullptr}; ///< PETSc linear solver + Mat Jmf{nullptr}; ///< Matrix Free Jacobian + Mat Jfd{nullptr}; ///< Finite Difference Jacobian (brute-force, when not using coloring) + PetscPreconditioner + petsc_preconditioner; ///< Coloring-based FD Jacobian + MatFDColoring BoutReal next_output; ///< When the monitor should be called next - PetscBool interpolate{PETSC_TRUE}; ///< Whether to interpolate or not + bool interpolate; ///< Interpolate to regular times? + + bool diagnose; ///< If true, print some information about current stage + bool user_precon; ///< Use user-supplied preconditioning function? + + BoutReal atol; ///< Absolute tolerance + BoutReal rtol; ///< Relative tolerance + BoutReal stol; ///< Convergence tolerance + + int maxnl; ///< Maximum nonlinear iterations per SNES solve + int maxf; ///< Maximum number of function evaluations allowed in the solver (default: 10000) + int maxl; ///< Maximum linear iterations + + std::string ts_type; ///< PETSc TS time solver type + std::string adapt_type; ///< TSAdaptType timestep adaptation + std::string snes_type; ///< PETSc SNES nonlinear solver type + std::string ksp_type; ///< PETSc KSP linear solver type + std::string pc_type; ///< Preconditioner type + std::string pc_hypre_type; ///< Hypre preconditioner type + std::string line_search_type; ///< Line search type + + bool matrix_free; ///< Use matrix free Jacobian + bool matrix_free_operator; ///< Use matrix free Jacobian in the operator? + int lag_jacobian; ///< Re-use Jacobian + bool use_coloring; ///< Use matrix coloring + void updateColoring(); ///< Updates the coloring using Jfd - char output_name[PETSC_MAX_PATH_LEN]; - PetscBool output_flag{PETSC_FALSE}; - PetscInt prev_linear_its; - BoutReal bout_snes_time{0.0}; - std::vector snes_list; + bool kspsetinitialguessnonzero; ///< Set initial guess to non-zero - bool adaptive; ///< Use adaptive timestepping - bool use_precon, use_jacobian; - BoutReal abstol, reltol; - bool adams_moulton; BoutReal start_timestep; int mxstep; }; diff --git a/src/solver/impls/power/power.cxx b/src/solver/impls/power/power.cxx index 888338b94c..744bdfe46e 100644 --- a/src/solver/impls/power/power.cxx +++ b/src/solver/impls/power/power.cxx @@ -3,19 +3,16 @@ #include "power.hxx" #include -#include +#include #include #include -#include - PowerSolver::PowerSolver(Options* opts) : Solver(opts), curtime((*options)["curtime"].doc("Simulation time (fixed)").withDefault(0.0)) {} int PowerSolver::init() { - TRACE("Initialising Power solver"); Solver::init(); output << "\n\tPower eigenvalue solver\n"; @@ -44,12 +41,11 @@ int PowerSolver::init() { } int PowerSolver::run() { - TRACE("PowerSolver::run()"); // Make sure that f0 has a norm of 1 divide(f0, norm(f0)); - for (int s = 0; s < getNumberOutputSteps(); s++) { + for (int s = 1; s <= getNumberOutputSteps(); s++) { load_vars(std::begin(f0)); run_rhs(curtime); diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 63d3c11753..ba4dce5849 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -5,7 +5,7 @@ * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu * * Contact Ben Dudson, bd512@york.ac.uk - * + * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with BOUT++. If not, see . - * + * **************************************************************************/ #include "bout/build_defines.hxx" @@ -31,23 +31,58 @@ #include #include +#include +#include #include #include +#include +#include #include #include -#include "bout/unused.hxx" - +#include #include // use CVSPGMR linear solver each internal step #include // contains the enum for types of preconditioning #include // band preconditioner function prototypes +#include "fmt/format.h" + +#include +#include +#include + using namespace pvode; void solver_f(integer N, BoutReal t, N_Vector u, N_Vector udot, void* f_data); void solver_gloc(integer N, BoutReal t, BoutReal* u, BoutReal* udot, void* f_data); void solver_cfn(integer N, BoutReal t, N_Vector u, void* f_data); +namespace { +// local only +void pvode_load_data_f3d(const std::vector& evolve_bndrys, + std::vector& ffs, const BoutReal* udata) { + int p = 0; + const Mesh* mesh = ffs[0].getMesh(); + const int nz = mesh->LocalNz; + for (const auto& bndry : {true, false}) { + for (const auto& i2d : mesh->getRegion2D(bndry ? "RGN_BNDRY" : "RGN_NOBNDRY")) { + for (int jz = 0; jz < nz; jz++) { + // Loop over 3D variables + auto evolve_bndry = evolve_bndrys.cbegin(); + for (auto ff = ffs.begin(); ff != ffs.end(); ++ff) { + if (bndry && !*evolve_bndry) { + continue; + } + (*ff)[mesh->ind2Dto3D(i2d, jz)] = udata[p]; + p++; + } + ++evolve_bndry; + } + } + } +} +} // namespace + const BoutReal ZERO = 0.0; long int iopt[OPT_SIZE]; @@ -90,7 +125,6 @@ PvodeSolver::~PvodeSolver() { **************************************************************************/ int PvodeSolver::init() { - TRACE("Initialising PVODE solver"); int mudq, mldq, mukeep, mlkeep; boole optIn; @@ -161,8 +195,8 @@ int PvodeSolver::init() { BoutReal* udata = N_VDATA(u); save_vars(udata); - /* Call CVodeMalloc to initialize CVODE: - + /* Call CVodeMalloc to initialize CVODE: + neq is the problem size = number of equations f is the user's right hand side function in y'=f(t,y) T0 is the initial time @@ -185,10 +219,47 @@ int PvodeSolver::init() { for (i = 0; i < OPT_SIZE; i++) { ropt[i] = ZERO; } + /* iopt[MXSTEP] : maximum number of internal steps to be taken by * + * the solver in its attempt to reach tout. * + * Optional input. (Default = 500). */ iopt[MXSTEP] = pvode_mxstep; - cvode_mem = CVodeMalloc(neq, solver_f, simtime, u, BDF, NEWTON, SS, &reltol, &abstol, - this, nullptr, optIn, iopt, ropt, machEnv); + { + /* ropt[H0] : initial step size. Optional input. */ + + /* ropt[HMAX] : maximum absolute value of step size allowed. * + * Optional input. (Default is infinity). */ + const BoutReal hmax( + (*options)["max_timestep"].doc("Maximum internal timestep").withDefault(-1.)); + if (hmax > 0) { + ropt[HMAX] = hmax; + } + /* ropt[HMIN] : minimum absolute value of step size allowed. * + * Optional input. (Default is 0.0). */ + const BoutReal hmin( + (*options)["min_timestep"].doc("Minimum internal timestep").withDefault(-1.)); + if (hmin > 0) { + ropt[HMIN] = hmin; + } + /* iopt[MAXORD] : maximum lmm order to be used by the solver. * + * Optional input. (Default = 12 for ADAMS, 5 for * + * BDF). */ + const int maxOrder((*options)["max_order"].doc("Maximum order").withDefault(-1)); + if (maxOrder > 0) { + iopt[MAXORD] = maxOrder; + } + } + const bool use_adam((*options)["adams_moulton"] + .doc("Use Adams Moulton solver instead of BDF") + .withDefault(false)); + + debug_on_failure = + (*options)["debug_on_failure"] + .doc("Run an aditional rhs if the solver fails with extra tracking") + .withDefault(false); + + cvode_mem = CVodeMalloc(neq, solver_f, simtime, u, use_adam ? ADAMS : BDF, NEWTON, SS, + &reltol, &abstol, this, nullptr, optIn, iopt, ropt, machEnv); if (cvode_mem == nullptr) { throw BoutException("\tError: CVodeMalloc failed.\n"); @@ -218,13 +289,12 @@ int PvodeSolver::init() { **************************************************************************/ int PvodeSolver::run() { - TRACE("PvodeSolver::run()"); if (!pvode_initialised) { throw BoutException("PvodeSolver not initialised\n"); } - for (int i = 0; i < getNumberOutputSteps(); i++) { + for (int i = 1; i <= getNumberOutputSteps(); i++) { /// Run the solver for one output timestep simtime = run(simtime + getOutputTimestep()); @@ -293,7 +363,43 @@ BoutReal PvodeSolver::run(BoutReal tout) { // Check return flag if (flag != SUCCESS) { output_error.write("ERROR CVODE step failed, flag = {:d}\n", flag); - return (-1.0); + if (debug_on_failure) { + const CVodeMemRec* cv_mem = static_cast(cvode_mem); + if (!(f2d.empty() and v2d.empty() and v3d.empty())) { + output_warn.write("debug_on_failure is currently only supported for Field3Ds"); + return -1.0; + } + auto debug_ptr = std::make_shared(); + Options& debug = *debug_ptr; + using namespace std::string_literals; + Mesh* mesh{}; + for (const auto& prefix : {"pre_"s, "residuum_"s}) { + std::vector list_of_fields{}; + std::vector evolve_bndrys{}; + for (const auto& f : f3d) { + mesh = f.var->getMesh(); + Field3D to_load{0., mesh}; + to_load.setLocation(f.location); + debug[fmt::format("{:s}{:s}", prefix, f.name)] = to_load; + list_of_fields.push_back(to_load); + evolve_bndrys.push_back(f.evolve_bndry); + } + pvode_load_data_f3d(evolve_bndrys, list_of_fields, + prefix == "pre_"s ? udata : N_VDATA(cv_mem->cv_acor)); + } + + for (auto& f : f3d) { + f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug_ptr); + } + run_rhs(simtime); + + for (auto& f : f3d) { + saveParallel(debug, f.name, *f.var); + } + bout::OptionsIO::write("BOUT.debug", debug, mesh); + MPI_Barrier(BoutComm::get()); + } + return -1.0; } return simtime; @@ -303,7 +409,8 @@ BoutReal PvodeSolver::run(BoutReal tout) { * RHS function **************************************************************************/ -void PvodeSolver::rhs(int UNUSED(N), BoutReal t, BoutReal* udata, BoutReal* dudata) { +void PvodeSolver::rhs([[maybe_unused]] int N, BoutReal t, BoutReal* udata, + BoutReal* dudata) { TRACE("Running RHS: PvodeSolver::rhs({})", t); // Get current timestep @@ -319,7 +426,8 @@ void PvodeSolver::rhs(int UNUSED(N), BoutReal t, BoutReal* udata, BoutReal* duda save_derivs(dudata); } -void PvodeSolver::gloc(int UNUSED(N), BoutReal t, BoutReal* udata, BoutReal* dudata) { +void PvodeSolver::gloc([[maybe_unused]] int N, BoutReal t, BoutReal* udata, + BoutReal* dudata) { TRACE("Running RHS: PvodeSolver::gloc({})", t); Timer timer("rhs"); @@ -360,8 +468,8 @@ void solver_gloc(integer N, BoutReal t, BoutReal* u, BoutReal* udot, void* f_dat } // Preconditioner communication function -void solver_cfn(integer UNUSED(N), BoutReal UNUSED(t), N_Vector UNUSED(u), - void* UNUSED(f_data)) { +void solver_cfn([[maybe_unused]] integer N, [[maybe_unused]] BoutReal t, + [[maybe_unused]] N_Vector u, [[maybe_unused]] void* f_data) { // doesn't do anything at the moment } diff --git a/src/solver/impls/pvode/pvode.hxx b/src/solver/impls/pvode/pvode.hxx index 6425fc1868..cf68444b6c 100644 --- a/src/solver/impls/pvode/pvode.hxx +++ b/src/solver/impls/pvode/pvode.hxx @@ -75,10 +75,15 @@ private: pvode::machEnvType machEnv{nullptr}; void* cvode_mem{nullptr}; - BoutReal abstol, reltol; // addresses passed in init must be preserved + BoutReal abstol, reltol; + // addresses passed in init must be preserved pvode::PVBBDData pdata{nullptr}; - bool pvode_initialised = false; + /// is pvode already initialised? + bool pvode_initialised{false}; + + /// Add debugging data if solver fails + bool debug_on_failure{false}; }; #endif // BOUT_PVODE_SOLVER_H diff --git a/src/solver/impls/rk3-ssp/rk3-ssp.cxx b/src/solver/impls/rk3-ssp/rk3-ssp.cxx index e13d996c00..26319eaa9a 100644 --- a/src/solver/impls/rk3-ssp/rk3-ssp.cxx +++ b/src/solver/impls/rk3-ssp/rk3-ssp.cxx @@ -3,10 +3,8 @@ #include #include -#include #include #include -#include #include @@ -28,7 +26,6 @@ void RK3SSP::setMaxTimestep(BoutReal dt) { } int RK3SSP::init() { - TRACE("Initialising RK3 SSP solver"); Solver::init(); output << "\n\tRunge-Kutta 3rd-order SSP solver\n"; @@ -63,9 +60,8 @@ int RK3SSP::init() { } int RK3SSP::run() { - TRACE("RK3SSP::run()"); - for (int s = 0; s < getNumberOutputSteps(); s++) { + for (int s = 1; s <= getNumberOutputSteps(); s++) { BoutReal target = simtime + getOutputTimestep(); BoutReal dt; diff --git a/src/solver/impls/rk4/rk4.cxx b/src/solver/impls/rk4/rk4.cxx index 0e7a942a45..bb7c14fab2 100644 --- a/src/solver/impls/rk4/rk4.cxx +++ b/src/solver/impls/rk4/rk4.cxx @@ -3,14 +3,12 @@ #include #include -#include #include +#include #include #include -#include - RK4Solver::RK4Solver(Options* opts) : Solver(opts), atol((*options)["atol"].doc("Absolute tolerance").withDefault(1.e-5)), rtol((*options)["rtol"].doc("Relative tolerance").withDefault(1.e-3)), @@ -39,8 +37,6 @@ void RK4Solver::setMaxTimestep(BoutReal dt) { int RK4Solver::init() { - TRACE("Initialising RK4 solver"); - Solver::init(); output << "\n\tRunge-Kutta 4th-order solver\n"; @@ -77,9 +73,7 @@ int RK4Solver::init() { } int RK4Solver::run() { - TRACE("RK4Solver::run()"); - - for (int s = 0; s < getNumberOutputSteps(); s++) { + for (int s = 1; s <= getNumberOutputSteps(); s++) { BoutReal target = simtime + getOutputTimestep(); BoutReal dt; diff --git a/src/solver/impls/rkgeneric/rkgeneric.cxx b/src/solver/impls/rkgeneric/rkgeneric.cxx index 1c332d26de..20f4cc39d6 100644 --- a/src/solver/impls/rkgeneric/rkgeneric.cxx +++ b/src/solver/impls/rkgeneric/rkgeneric.cxx @@ -4,12 +4,8 @@ #include #include -#include -#include - -#include - #include +#include RKGenericSolver::RKGenericSolver(Options* opts) : Solver(opts), atol((*options)["atol"].doc("Absolute tolerance").withDefault(1.e-5)), @@ -40,8 +36,6 @@ void RKGenericSolver::setMaxTimestep(BoutReal dt) { int RKGenericSolver::init() { - TRACE("Initialising RKGeneric solver"); - Solver::init(); output << "\n\tRunge-Kutta generic solver with scheme type " << scheme->getType() << "\n"; @@ -86,9 +80,7 @@ void RKGenericSolver::resetInternalFields() { } int RKGenericSolver::run() { - TRACE("RKGenericSolver::run()"); - - for (int s = 0; s < getNumberOutputSteps(); s++) { + for (int s = 1; s <= getNumberOutputSteps(); s++) { BoutReal target = simtime + getOutputTimestep(); BoutReal dt; diff --git a/src/solver/impls/slepc/slepc.cxx b/src/solver/impls/slepc/slepc.cxx index 13657a4553..9887f184eb 100644 --- a/src/solver/impls/slepc/slepc.cxx +++ b/src/solver/impls/slepc/slepc.cxx @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -241,8 +240,6 @@ SlepcSolver::~SlepcSolver() { int SlepcSolver::init() { - TRACE("Initialising SLEPc solver"); - // Report initialisation output.write("Initialising SLEPc solver\n"); if (selfSolve) { @@ -582,7 +579,7 @@ void SlepcSolver::monitor(PetscInt its, PetscInt nconv, PetscScalar eigr[], static bool first = true; if (eigenValOnly && first) { first = false; - resetIterationCounter(); + resetIterationCounter(1); } // Temporary eigenvalues, converted from the SLEPc eigenvalues @@ -704,7 +701,7 @@ void SlepcSolver::analyseResults() { output << "Converged eigenvalues :\n" "\tIndex\tSlepc eig (mag.)\t\t\tBOUT eig (mag.)\n"; - resetIterationCounter(); + resetIterationCounter(1); // Declare and create vectors to store eigenfunctions Vec vecReal, vecImag; diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index e382bbd3f8..07d16652f2 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -4,20 +4,34 @@ #include "snes.hxx" +#include +#include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include #include +#include #include -#include - +#include "petscerror.h" #include "petscmat.h" +#include "petscpc.h" #include "petscsnes.h" +#include "petscsys.h" +#include "petscsystypes.h" +#include "petscvec.h" +namespace { /* * PETSc callback function, which evaluates the nonlinear * function to be solved by SNES. @@ -25,7 +39,7 @@ * This function assumes the context void pointer is a pointer * to an SNESSolver object. */ -static PetscErrorCode FormFunction(SNES UNUSED(snes), Vec x, Vec f, void* ctx) { +PetscErrorCode FormFunction(SNES UNUSED(snes), Vec x, Vec f, void* ctx) { return static_cast(ctx)->snes_function(x, f, false); } @@ -34,7 +48,7 @@ static PetscErrorCode FormFunction(SNES UNUSED(snes), Vec x, Vec f, void* ctx) { * * This function can be a linearised form of FormFunction */ -static PetscErrorCode FormFunctionForDifferencing(void* ctx, Vec x, Vec f) { +PetscErrorCode FormFunctionForDifferencing(void* ctx, Vec x, Vec f) { return static_cast(ctx)->snes_function(x, f, true); } @@ -43,30 +57,173 @@ static PetscErrorCode FormFunctionForDifferencing(void* ctx, Vec x, Vec f) { * * This can be a linearised and simplified form of FormFunction */ -static PetscErrorCode FormFunctionForColoring(SNES UNUSED(snes), Vec x, Vec f, - void* ctx) { +PetscErrorCode FormFunctionForColoring(void* UNUSED(snes), Vec x, Vec f, void* ctx) { return static_cast(ctx)->snes_function(x, f, true); } -static PetscErrorCode snesPCapply(PC pc, Vec x, Vec y) { - int ierr; - +PetscErrorCode snesPCapply(PC pc, Vec x, Vec y) { // Get the context SNESSolver* s; - ierr = PCShellGetContext(pc, reinterpret_cast(&s)); - CHKERRQ(ierr); + PetscCall(PCShellGetContext(pc, reinterpret_cast(&s))); PetscFunctionReturn(s->precon(x, y)); } +PetscErrorCode ComputeJacobianScaledColor(SNES snes, Vec x1, Mat Jac, Mat Jac_new, + void* ctx); +} // namespace + +PetscErrorCode SNESSolver::FDJinitialise() { + if (use_coloring) { + Field3D index = globalIndex(0); + PetscCall(petsc_preconditioner.createJacobianPattern( + index, *options, nlocal, n2Dvars(), n3Dvars(), BoutComm::get())); + output_progress.write("Creating Jacobian coloring\n"); + PetscCall(petsc_preconditioner.updateColoring(FormFunctionForColoring, this)); + + if (matrix_free_operator) { + PetscCall(SNESSetJacobian(snes, Jmf, petsc_preconditioner.jacobian(), + ComputeJacobianScaledColor, + petsc_preconditioner.coloring())); + } else { + PetscCall(SNESSetJacobian( + snes, petsc_preconditioner.jacobian(), petsc_preconditioner.jacobian(), + ComputeJacobianScaledColor, petsc_preconditioner.coloring())); + } + + if (prune_jacobian) { + // Will remove small elements from the Jacobian. + // Save a copy to recover from over-pruning + PetscCall(MatDuplicate(petsc_preconditioner.jacobian(), MAT_SHARE_NONZERO_PATTERN, + &Jfd_original)); + } + } else { + // Brute force calculation + // There is usually no reason to use this, except as a check of + // the coloring calculation. + + MatCreateAIJ( + BoutComm::get(), nlocal, nlocal, // Local sizes + PETSC_DETERMINE, PETSC_DETERMINE, // Global sizes + 3, // Number of nonzero entries in diagonal portion of local submatrix + nullptr, + 0, // Number of nonzeros per row in off-diagonal portion of local submatrix + nullptr, &Jfd); + + if (matrix_free_operator) { + SNESSetJacobian(snes, Jmf, Jfd, SNESComputeJacobianDefault, this); + } else { + SNESSetJacobian(snes, Jfd, Jfd, SNESComputeJacobianDefault, this); + } + + MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE); + } + return PETSC_SUCCESS; +} + +PetscErrorCode SNESSolver::FDJpruneJacobian() { +#if PETSC_VERSION_GE(3, 20, 0) + if (!use_coloring) { + throw BoutException("Jacobian pruning requires solver:use_coloring=true"); + } + + Mat Jfd = petsc_preconditioner.jacobian(); + + // Remove small elements from the Jacobian and recompute the coloring + // Only do this if there are a significant number of small elements. + int small_elements = 0; + int total_elements = 0; + + // Get index of rows owned by this processor + int rstart, rend; + MatGetOwnershipRange(Jfd, &rstart, &rend); + + PetscInt ncols; + const PetscScalar* vals; + for (int row = rstart; row < rend; row++) { + MatGetRow(Jfd, row, &ncols, nullptr, &vals); + for (int col = 0; col < ncols; col++) { + if (std::abs(vals[col]) < prune_abstol) { + ++small_elements; + } + ++total_elements; + } + MatRestoreRow(Jfd, row, &ncols, nullptr, &vals); + } + + if (small_elements > prune_fraction * total_elements) { + if (diagnose) { + output.write("\nPruning Jacobian elements: {} / {}\n", small_elements, + total_elements); + } + + // Prune Jacobian, keeping diagonal elements + PetscCall(MatFilter(Jfd, prune_abstol, PETSC_TRUE, PETSC_TRUE)); + + // Update the coloring from Jfd matrix + PetscCall(petsc_preconditioner.updateColoring(FormFunctionForColoring, this)); + if (matrix_free_operator) { + PetscCall(SNESSetJacobian(snes, Jmf, petsc_preconditioner.jacobian(), + ComputeJacobianScaledColor, + petsc_preconditioner.coloring())); + } else { + PetscCall(SNESSetJacobian( + snes, petsc_preconditioner.jacobian(), petsc_preconditioner.jacobian(), + ComputeJacobianScaledColor, petsc_preconditioner.coloring())); + } + + // Mark the Jacobian as pruned. This is so that it is only restored if pruned. + jacobian_pruned = true; + } +#endif // PETSC_VERSION_GE(3,20,0) + return PETSC_SUCCESS; +} + +PetscErrorCode SNESSolver::FDJrestoreFromPruning() { + if (!use_coloring) { + throw BoutException("Jacobian pruning requires solver:use_coloring=true"); + } + + Mat Jfd = petsc_preconditioner.jacobian(); + + // Restore pruned non-zero elements + PetscCall(MatCopy(Jfd_original, Jfd, DIFFERENT_NONZERO_PATTERN)); + // The non-zero pattern has changed, so update coloring + PetscCall(petsc_preconditioner.updateColoring(FormFunctionForColoring, this)); + if (matrix_free_operator) { + PetscCall(SNESSetJacobian(snes, Jmf, petsc_preconditioner.jacobian(), + ComputeJacobianScaledColor, + petsc_preconditioner.coloring())); + } else { + PetscCall(SNESSetJacobian(snes, petsc_preconditioner.jacobian(), + petsc_preconditioner.jacobian(), ComputeJacobianScaledColor, + petsc_preconditioner.coloring())); + } + jacobian_pruned = false; // Reset flag. Will be set after pruning. + return PETSC_SUCCESS; +} + SNESSolver::SNESSolver(Options* opts) : Solver(opts), + output_trigger( + (*options)["output_trigger"] + .doc("Decides when to save outputs. fixed_time_interval, residual_ratio") + .withDefault(BoutSnesOutput::fixed_time_interval)), + output_residual_ratio( + (*options)["output_residual_ratio"] + .doc("Trigger an output when residual falls by this ratio") + .withDefault(0.5)), timestep( (*options)["timestep"].doc("Initial backward Euler timestep").withDefault(1.0)), dt_min_reset((*options)["dt_min_reset"] .doc("If dt falls below this, reset to starting dt") .withDefault(1e-6)), max_timestep((*options)["max_timestep"].doc("Maximum timestep").withDefault(1e37)), + equation_form( + (*options)["equation_form"] + .doc("Form of equation to solve: rearranged_backward_euler (default);" + " pseudo_transient; backward_euler; direct_newton") + .withDefault(BoutSnesEquationForm::rearranged_backward_euler)), snes_type((*options)["snes_type"] .doc("PETSc nonlinear solver method to use") .withDefault("anderson")), @@ -76,6 +233,9 @@ SNESSolver::SNESSolver(Options* opts) .doc("Convergence tolerance in terms of the norm of the change in " "the solution between steps") .withDefault(1e-8)), + maxf((*options)["maxf"] + .doc("Maximum number of function evaluations per SNES solve") + .withDefault(10000)), maxits((*options)["max_nonlinear_iterations"] .doc("Maximum number of nonlinear iterations per SNES solve") .withDefault(50)), @@ -85,16 +245,61 @@ SNESSolver::SNESSolver(Options* opts) upper_its((*options)["upper_its"] .doc("Iterations above which the next timestep is reduced") .withDefault(static_cast(maxits * 0.8))), + max_snes_failures((*options)["max_snes_failures"] + .doc("Abort after this number of consecutive failures") + .withDefault(10)), + timestep_factor_on_failure((*options)["timestep_factor_on_failure"] + .doc("Multiply timestep on convergence failure") + .withDefault(0.5)), + timestep_factor_on_upper_its( + (*options)["timestep_factor_on_upper_its"] + .doc("Multiply timestep if iterations exceed upper_its") + .withDefault(0.9)), + timestep_factor_on_lower_its( + (*options)["timestep_factor_on_lower_its"] + .doc("Multiply timestep if iterations are below lower_its") + .withDefault(1.4)), + pseudo_strategy((*options)["pseudo_strategy"] + .doc("PTC strategy to use when setting timesteps") + .withDefault(BoutPTCStrategy::inverse_residual)), + pseudo_alpha((*options)["pseudo_alpha"] + .doc("Sets timestep using dt = alpha / residual") + .withDefault(100. * atol * timestep)), + pseudo_alpha_minimum((*options)["pseudo_alpha_minimum"] + .doc("Minimum value of pseudo_alpha") + .withDefault(0.1 * pseudo_alpha)), + pseudo_growth_factor((*options)["pseudo_growth_factor"] + .doc("PTC growth factor on success") + .withDefault(1.1)), + pseudo_reduction_factor((*options)["pseudo_reduction_factor"] + .doc("PTC reduction factor on failure") + .withDefault(0.5)), + pseudo_max_ratio((*options)["pseudo_max_ratio"] + .doc("PTC maximum timestep ratio between neighbors") + .withDefault(2.)), + timestep_control((*options)["timestep_control"] + .doc("Timestep control method") + .withDefault(BoutSnesTimestep::pid_nonlinear_its)), + timestep_factor((*options)["timestep_factor"] + .doc("When timestep_control=residual_ratio, multiply timestep " + "by this factor each step") + .withDefault(1.1)), + target_its((*options)["target_its"].doc("Target snes iterations").withDefault(7)), + kP((*options)["kP"].doc("Proportional PID parameter").withDefault(0.7)), + kI((*options)["kI"].doc("Integral PID parameter").withDefault(0.3)), + kD((*options)["kD"].doc("Derivative PID parameter").withDefault(0.2)), + pid_consider_failures( + (*options)["pid_consider_failures"] + .doc("Reduce timestep increases if recent solves have failed") + .withDefault(false)), + last_failure_weight((*options)["last_failure_weight"] + .doc("Weighting of last timestep in recent failure rate") + .withDefault(0.1)), diagnose( (*options)["diagnose"].doc("Print additional diagnostics").withDefault(false)), diagnose_failures((*options)["diagnose_failures"] .doc("Print more diagnostics when SNES fails") .withDefault(false)), - equation_form( - (*options)["equation_form"] - .doc("Form of equation to solve: rearranged_backward_euler (default);" - " pseudo_transient; backward_euler; direct_newton") - .withDefault(BoutSnesEquationForm::rearranged_backward_euler)), predictor((*options)["predictor"].doc("Use linear predictor?").withDefault(true)), use_precon((*options)["use_precon"] .doc("Use user-supplied preconditioner?") @@ -122,10 +327,14 @@ SNESSolver::SNESSolver(Options* opts) .withDefault(false)), matrix_free_operator((*options)["matrix_free_operator"] .doc("Use matrix free Jacobian-vector operator?") - .withDefault(true)), + .withDefault(false)), lag_jacobian((*options)["lag_jacobian"] .doc("Re-use the Jacobian this number of SNES iterations") .withDefault(50)), + jacobian_persists( + (*options)["jacobian_persists"] + .doc("Re-use Jacobian and preconditioner across nonlinear solves") + .withDefault(false)), use_coloring((*options)["use_coloring"] .doc("Use matrix coloring to calculate Jacobian?") .withDefault(true)), @@ -143,13 +352,21 @@ SNESSolver::SNESSolver(Options* opts) .doc("Scale time derivatives (Jacobian row scaling)?") .withDefault(false)), scale_vars((*options)["scale_vars"] - .doc("Scale variables (Jacobian column scaling)?") + .doc("Scale variables to be order unity?") + .withDefault(false)), + rescale_period( + (*options)["rescale_period"] + .doc("Number of iterations before recalculating variable scaling") + .withDefault(30)), + rescale_threshold((*options)["rescale_threshold"] + .doc("How much change their should be in the norm of the " + "state, before rescaling") + .withDefault(100.)), + asinh_vars((*options)["asinh_vars"] + .doc("Apply asinh() to all variables?") .withDefault(false)) {} int SNESSolver::init() { - - TRACE("Initialising SNES solver"); - Solver::init(); output << "\n\tSNES steady state solver\n"; @@ -159,7 +376,8 @@ int SNESSolver::init() { // Get total problem size int ntmp; if (bout::globals::mpi->MPI_Allreduce(&nlocal, &ntmp, 1, MPI_INT, MPI_SUM, - BoutComm::get())) { + BoutComm::get()) + != 0) { throw BoutException("MPI_Allreduce failed!"); } neq = ntmp; @@ -167,56 +385,69 @@ int SNESSolver::init() { output_info.write("\t3d fields = {:d}, 2d fields = {:d} neq={:d}, local_N={:d}\n", n3Dvars(), n2Dvars(), neq, nlocal); + // Initialise fields for storing residual of nonlinear solves + if (diagnose) { + for (const auto& f : f2d) { + resid_2d.emplace_back(emptyFrom(*f.var)); + } + for (const auto& f : f3d) { + resid_3d.emplace_back(emptyFrom(*f.var)); + } + } + // Initialise PETSc components - int ierr; // Vectors output_info.write("Creating vector\n"); - ierr = VecCreate(BoutComm::get(), &snes_x); - CHKERRQ(ierr); - ierr = VecSetSizes(snes_x, nlocal, PETSC_DECIDE); - CHKERRQ(ierr); - ierr = VecSetFromOptions(snes_x); - CHKERRQ(ierr); - - VecDuplicate(snes_x, &snes_f); - VecDuplicate(snes_x, &x0); - - if (equation_form == BoutSnesEquationForm::rearranged_backward_euler) { - // Need an intermediate vector for rearranged Backward Euler - ierr = VecDuplicate(snes_x, &delta_x); - CHKERRQ(ierr); + PetscCall(VecCreate(BoutComm::get(), &snes_x)); + PetscCall(VecSetSizes(snes_x, nlocal, PETSC_DECIDE)); + PetscCall(VecSetFromOptions(snes_x)); + + PetscCall(VecDuplicate(snes_x, &snes_f)); + PetscCall(VecDuplicate(snes_x, &x0)); + if (diagnose) { + PetscCall(VecDuplicate(snes_f, &f0)); + PetscCall(VecDuplicate(snes_f, &deriv)); + } + + if ((equation_form == BoutSnesEquationForm::rearranged_backward_euler) + || (equation_form == BoutSnesEquationForm::pseudo_transient)) { + // Need an intermediate vector for rearranged Backward Euler or Pseudo-Transient Continuation + PetscCall(VecDuplicate(snes_x, &delta_x)); } if (predictor) { // Storage for previous solution - ierr = VecDuplicate(snes_x, &x1); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &x1)); } if (scale_rhs) { // Storage for rhs factors, one per evolving variable - ierr = VecDuplicate(snes_x, &rhs_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &rhs_scaling_factors)); // Set all factors to 1 to start with - ierr = VecSet(rhs_scaling_factors, 1.0); - CHKERRQ(ierr); + PetscCall(VecSet(rhs_scaling_factors, 1.0)); // Array to store inverse Jacobian row norms - ierr = VecDuplicate(snes_x, &jac_row_inv_norms); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &jac_row_inv_norms)); } if (scale_vars) { // Storage for var factors, one per evolving variable - ierr = VecDuplicate(snes_x, &var_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &var_scaling_factors)); // Set all factors to 1 to start with - ierr = VecSet(var_scaling_factors, 1.0); - CHKERRQ(ierr); + PetscCall(VecSet(var_scaling_factors, 1.0)); // Storage for scaled 'x' state vectors - ierr = VecDuplicate(snes_x, &scaled_x); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &scaled_x)); + } else if (asinh_vars) { + PetscCall(VecDuplicate(snes_x, &scaled_x)); + } + + if (equation_form == BoutSnesEquationForm::pseudo_transient) { + PetscCall(initPseudoTimestepping()); } + // Per-cell residuals + local_residual = 0.0; + local_residual_2d = 0.0; + global_residual = 0.0; // Nonlinear solver interface (SNES) output_info.write("Create SNES\n"); @@ -259,410 +490,37 @@ int SNESSolver::init() { SNESSetJacobian(snes, Jmf, Jmf, MatMFFDComputeJacobian, this); } else { - // Calculate the Jacobian using finite differences. - // The finite difference Jacobian (Jfd) may be used for both operator - // and preconditioner or, if matrix_free_operator, in only the preconditioner. - if (use_coloring) { - // Use matrix coloring - // This greatly reduces the number of times the rhs() function needs - // to be evaluated when calculating the Jacobian. - - // Use global mesh for now - Mesh* mesh = bout::globals::mesh; - - ////////////////////////////////////////////////// - // Get the local indices by starting at 0 - Field3D index = globalIndex(0); - - ////////////////////////////////////////////////// - // Pre-allocate PETSc storage - - output_progress.write("Setting Jacobian matrix sizes\n"); - - int n2d = f2d.size(); - int n3d = f3d.size(); - - // Set size of Matrix on each processor to nlocal x nlocal - MatCreate(BoutComm::get(), &Jfd); - MatSetSizes(Jfd, nlocal, nlocal, PETSC_DETERMINE, PETSC_DETERMINE); - MatSetFromOptions(Jfd); - - std::vector d_nnz(nlocal); - std::vector o_nnz(nlocal); - - // Set values for most points - const int ncells_x = (mesh->LocalNx > 1) ? 2 : 0; - const int ncells_y = (mesh->LocalNy > 1) ? 2 : 0; - const int ncells_z = (mesh->LocalNz > 1) ? 2 : 0; - - const auto star_pattern = (1 + ncells_x + ncells_y) * (n3d + n2d) + ncells_z * n3d; - - // Offsets. Start with the central cell - std::vector> xyoffsets{{0, 0}}; - if (ncells_x != 0) { - // Stencil includes points in X - xyoffsets.push_back({-1, 0}); - xyoffsets.push_back({1, 0}); - } - if (ncells_y != 0) { - // Stencil includes points in Y - xyoffsets.push_back({0, -1}); - xyoffsets.push_back({0, 1}); - } - - output_info.write("Star pattern: {} non-zero entries\n", star_pattern); - for (int i = 0; i < nlocal; i++) { - // Non-zero elements on this processor - d_nnz[i] = star_pattern; - // Non-zero elements on neighboring processor - o_nnz[i] = 0; - } - - // X boundaries - if (ncells_x != 0) { - if (mesh->firstX()) { - // Lower X boundary - for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { - const int localIndex = ROUND(index(mesh->xstart, y, z)); - ASSERT2((localIndex >= 0) && (localIndex < nlocal)); - const int num_fields = (z == 0) ? n2d + n3d : n3d; - for (int i = 0; i < num_fields; i++) { - d_nnz[localIndex + i] -= (n3d + n2d); - } - } - } - } else { - // On another processor - for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { - const int localIndex = ROUND(index(mesh->xstart, y, z)); - ASSERT2((localIndex >= 0) && (localIndex < nlocal)); - const int num_fields = (z == 0) ? n2d + n3d : n3d; - for (int i = 0; i < num_fields; i++) { - d_nnz[localIndex + i] -= (n3d + n2d); - o_nnz[localIndex + i] += (n3d + n2d); - } - } - } - } - if (mesh->lastX()) { - // Upper X boundary - for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { - const int localIndex = ROUND(index(mesh->xend, y, z)); - ASSERT2((localIndex >= 0) && (localIndex < nlocal)); - const int num_fields = (z == 0) ? n2d + n3d : n3d; - for (int i = 0; i < num_fields; i++) { - d_nnz[localIndex + i] -= (n3d + n2d); - } - } - } - } else { - // On another processor - for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { - const int localIndex = ROUND(index(mesh->xend, y, z)); - ASSERT2((localIndex >= 0) && (localIndex < nlocal)); - const int num_fields = (z == 0) ? n2d + n3d : n3d; - for (int i = 0; i < num_fields; i++) { - d_nnz[localIndex + i] -= (n3d + n2d); - o_nnz[localIndex + i] += (n3d + n2d); - } - } - } - } - } - - // Y boundaries - if (ncells_y != 0) { - for (int x = mesh->xstart; x <= mesh->xend; x++) { - // Default to no boundary - // NOTE: This assumes that communications in Y are to other - // processors. If Y is communicated with this processor (e.g. NYPE=1) - // then this will result in PETSc warnings about out of range allocations - - // z = 0 case - int localIndex = ROUND(index(x, mesh->ystart, 0)); - ASSERT2(localIndex >= 0); - - // All 2D and 3D fields - for (int i = 0; i < n2d + n3d; i++) { - o_nnz[localIndex + i] += (n3d + n2d); - d_nnz[localIndex + i] -= (n3d + n2d); - } - - for (int z = 1; z < mesh->LocalNz; z++) { - localIndex = ROUND(index(x, mesh->ystart, z)); - - // Only 3D fields - for (int i = 0; i < n3d; i++) { - o_nnz[localIndex + i] += (n3d + n2d); - d_nnz[localIndex + i] -= (n3d + n2d); - } - } - - // z = 0 case - localIndex = ROUND(index(x, mesh->yend, 0)); - // All 2D and 3D fields - for (int i = 0; i < n2d + n3d; i++) { - o_nnz[localIndex + i] += (n3d + n2d); - d_nnz[localIndex + i] -= (n3d + n2d); - } - - for (int z = 1; z < mesh->LocalNz; z++) { - localIndex = ROUND(index(x, mesh->yend, z)); - - // Only 3D fields - for (int i = 0; i < n3d; i++) { - o_nnz[localIndex + i] += (n3d + n2d); - d_nnz[localIndex + i] -= (n3d + n2d); - } - } - } - - for (RangeIterator it = mesh->iterateBndryLowerY(); !it.isDone(); it++) { - // A boundary, so no communication - - // z = 0 case - int localIndex = ROUND(index(it.ind, mesh->ystart, 0)); - if (localIndex < 0) { - // This can occur because it.ind includes values in x boundary e.g. x=0 - continue; - } - // All 2D and 3D fields - for (int i = 0; i < n2d + n3d; i++) { - o_nnz[localIndex + i] -= (n3d + n2d); - } - - for (int z = 1; z < mesh->LocalNz; z++) { - int localIndex = ROUND(index(it.ind, mesh->ystart, z)); - - // Only 3D fields - for (int i = 0; i < n3d; i++) { - o_nnz[localIndex + i] -= (n3d + n2d); - } - } - } - - for (RangeIterator it = mesh->iterateBndryUpperY(); !it.isDone(); it++) { - // A boundary, so no communication - - // z = 0 case - const int localIndex = ROUND(index(it.ind, mesh->yend, 0)); - if (localIndex < 0) { - continue; // Out of domain - } - - // All 2D and 3D fields - for (int i = 0; i < n2d + n3d; i++) { - o_nnz[localIndex + i] -= (n3d + n2d); - } - - for (int z = 1; z < mesh->LocalNz; z++) { - const int localIndex = ROUND(index(it.ind, mesh->yend, z)); - - // Only 3D fields - for (int i = 0; i < n3d; i++) { - o_nnz[localIndex + i] -= (n3d + n2d); - } - } - } - } - - output_progress.write("Pre-allocating Jacobian\n"); - - // Pre-allocate - MatMPIAIJSetPreallocation(Jfd, 0, d_nnz.data(), 0, o_nnz.data()); - MatSeqAIJSetPreallocation(Jfd, 0, d_nnz.data()); - MatSetUp(Jfd); - MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE); - - // Determine which row/columns of the matrix are locally owned - int Istart, Iend; - MatGetOwnershipRange(Jfd, &Istart, &Iend); - - // Convert local into global indices - // Note: Not in the boundary cells, to keep -1 values - for (const auto& i : mesh->getRegion3D("RGN_NOBNDRY")) { - index[i] += Istart; - } - - // Now communicate to fill guard cells - mesh->communicate(index); - - ////////////////////////////////////////////////// - // Mark non-zero entries - - output_progress.write("Marking non-zero Jacobian entries\n"); - - const PetscScalar val = 1.0; - - for (int x = mesh->xstart; x <= mesh->xend; x++) { - for (int y = mesh->ystart; y <= mesh->yend; y++) { - - const int ind0 = ROUND(index(x, y, 0)); - - // 2D fields - for (int i = 0; i < n2d; i++) { - const PetscInt row = ind0 + i; - - // Loop through each point in the 5-point stencil - for (const auto& xyoffset : xyoffsets) { - const int xi = x + xyoffset.first; - const int yi = y + xyoffset.second; - - if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) - || (yi >= mesh->LocalNy)) { - continue; - } - - const int ind2 = ROUND(index(xi, yi, 0)); - - if (ind2 < 0) { - continue; // A boundary point - } - - // Depends on all variables on this cell - for (int j = 0; j < n2d; j++) { - const PetscInt col = ind2 + j; - ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); - CHKERRQ(ierr); - } - } - } - - // 3D fields - for (int z = 0; z < mesh->LocalNz; z++) { - - const int ind = ROUND(index(x, y, z)); - - for (int i = 0; i < n3d; i++) { - PetscInt row = ind + i; - if (z == 0) { - row += n2d; - } - - // Depends on 2D fields - for (int j = 0; j < n2d; j++) { - const PetscInt col = ind0 + j; - ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); - CHKERRQ(ierr); - } - - // Star pattern - for (const auto& xyoffset : xyoffsets) { - const int xi = x + xyoffset.first; - const int yi = y + xyoffset.second; - - if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) - || (yi >= mesh->LocalNy)) { - continue; - } - - int ind2 = ROUND(index(xi, yi, z)); - if (ind2 < 0) { - continue; // Boundary point - } - - if (z == 0) { - ind2 += n2d; - } - - // 3D fields on this cell - for (int j = 0; j < n3d; j++) { - const PetscInt col = ind2 + j; - ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); - if (ierr != 0) { - output.write("ERROR: {} : ({}, {}) -> ({}, {}) : {} -> {}\n", row, x, - y, xi, yi, ind2, ind2 + n3d - 1); - } - CHKERRQ(ierr); - } - } - - int nz = mesh->LocalNz; - if (nz > 1) { - // Multiple points in z - - int zp = (z + 1) % nz; - - int ind2 = ROUND(index(x, y, zp)); - if (zp == 0) { - ind2 += n2d; - } - for (int j = 0; j < n3d; j++) { - const PetscInt col = ind2 + j; - ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); - CHKERRQ(ierr); - } - - int zm = (z - 1 + nz) % nz; - ind2 = ROUND(index(x, y, zm)); - if (zm == 0) { - ind2 += n2d; - } - for (int j = 0; j < n3d; j++) { - const PetscInt col = ind2 + j; - ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); - CHKERRQ(ierr); - } - } - } - } - } - } - // Finished marking non-zero entries - - output_progress.write("Assembling Jacobian matrix\n"); - - // Assemble Matrix - MatAssemblyBegin(Jfd, MAT_FINAL_ASSEMBLY); - MatAssemblyEnd(Jfd, MAT_FINAL_ASSEMBLY); - - output_progress.write("Creating Jacobian coloring\n"); - updateColoring(); - - if (prune_jacobian) { - // Will remove small elements from the Jacobian. - // Save a copy to recover from over-pruning - ierr = MatDuplicate(Jfd, MAT_SHARE_NONZERO_PATTERN, &Jfd_original); - CHKERRQ(ierr); - } - } else { - // Brute force calculation - // There is usually no reason to use this, except as a check of - // the coloring calculation. - - MatCreateAIJ( - BoutComm::get(), nlocal, nlocal, // Local sizes - PETSC_DETERMINE, PETSC_DETERMINE, // Global sizes - 3, // Number of nonzero entries in diagonal portion of local submatrix - nullptr, - 0, // Number of nonzeros per row in off-diagonal portion of local submatrix - nullptr, &Jfd); - - if (matrix_free_operator) { - SNESSetJacobian(snes, Jmf, Jfd, SNESComputeJacobianDefault, this); - } else { - SNESSetJacobian(snes, Jfd, Jfd, SNESComputeJacobianDefault, this); - } - - MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE); + // Calculate the Jacobian using finite differences. The finite + // difference Jacobian (Jfd) may be used for both operator and + // preconditioner or, if matrix_free_operator, in only the + // preconditioner. + + // Create a vector to store interpolated output solution + // Used so that the timestep does not have to be adjusted, + // because that would require updating the preconditioner. + PetscCall(VecDuplicate(snes_x, &output_x)); + if (diagnose) { + PetscCall(VecDuplicate(snes_f, &output_f)); } + // Initialize the Finite Difference Jacobian + PetscCall(FDJinitialise()); + // Re-use Jacobian // Note: If the 'Amat' Jacobian is matrix free, SNESComputeJacobian // always updates its reference 'u' vector every nonlinear iteration SNESSetLagJacobian(snes, lag_jacobian); - // Set Jacobian and preconditioner to persist across time steps - SNESSetLagJacobianPersists(snes, PETSC_TRUE); - SNESSetLagPreconditionerPersists(snes, PETSC_TRUE); - SNESSetLagPreconditioner(snes, 1); // Rebuild when Jacobian is rebuilt + nl_its_prev = target_its; + nl_its_prev2 = target_its; + PetscCall( + SNESSetLagJacobianPersists(snes, static_cast(jacobian_persists))); + PetscCall(SNESSetLagPreconditionerPersists( + snes, static_cast(jacobian_persists))); + PetscCall(SNESSetLagPreconditioner(snes, 1)); // Rebuild when Jacobian is rebuilt } // Set tolerances - SNESSetTolerances(snes, atol, rtol, stol, maxits, PETSC_DEFAULT); + SNESSetTolerances(snes, atol, rtol, stol, maxits, maxf); // Force SNES to take at least one nonlinear iteration. // This may prevent the solver from getting stuck in false steady state conditions @@ -734,10 +592,10 @@ int SNESSolver::init() { SNESType snestype; SNESGetType(snes, &snestype); output_info.write("SNES Type : {}\n", snestype); - if (ksptype) { + if (ksptype != nullptr) { output_info.write("KSP Type : {}\n", ksptype); } - if (pctype) { + if (pctype != nullptr) { output_info.write("PC Type : {}\n", pctype); } } @@ -745,115 +603,187 @@ int SNESSolver::init() { return 0; } +PetscErrorCode SNESSolver::rescale(int& saved_jacobian_lag) { + // Individual variable scaling + // Note: If variables are rescaled then the Jacobian columns + // need to be scaled or recalculated + int istart = 0; + int iend = 0; + VecGetOwnershipRange(snes_x, &istart, &iend); + + // Take ownership of snes_x and var_scaling_factors data + PetscScalar* snes_x_data = nullptr; + PetscCall(VecGetArray(snes_x, &snes_x_data)); + PetscScalar* x1_data = nullptr; + if (predictor) { + // x1 is only allocated if predictor is enabled + PetscCall(VecGetArray(x1, &x1_data)); + } + PetscScalar* var_scaling_factors_data = nullptr; + PetscCall(VecGetArray(var_scaling_factors, &var_scaling_factors_data)); + + // Normalise each value in the state + // Limit normalisation so scaling factor is never smaller than rtol + for (int i = 0; i < iend - istart; ++i) { + const PetscScalar norm = + BOUTMAX(std::abs(snes_x_data[i]), rtol / var_scaling_factors_data[i]); + snes_x_data[i] /= norm; + if (predictor) { + x1_data[i] /= norm; // Update history for predictor + } + var_scaling_factors_data[i] *= norm; + } + + // Restore vector underlying data + PetscCall(VecRestoreArray(var_scaling_factors, &var_scaling_factors_data)); + if (predictor) { + PetscCall(VecRestoreArray(x1, &x1_data)); + } + PetscCall(VecRestoreArray(snes_x, &snes_x_data)); + + if (diagnose) { + // Print maximum and minimum scaling factors + PetscReal max_scale = 0.; + PetscReal min_scale = 0.; + VecMax(var_scaling_factors, nullptr, &max_scale); + VecMin(var_scaling_factors, nullptr, &min_scale); + output.write("Var scaling: {} -> {}\n", min_scale, max_scale); + } + + // Force recalculation of the Jacobian + SNESGetLagJacobian(snes, &saved_jacobian_lag); + // FIXME: This isn't actually what we want to happen. The lag should + // stay as before, we just want to make sure the Jacobian gets + // re-evaluated at the start of the solve. We need to use + // SNESSetJacobianPersist for that. + SNESSetLagJacobian(snes, 1); + return PETSC_SUCCESS; +} + int SNESSolver::run() { - TRACE("SNESSolver::run()"); - int ierr; // Set initial guess at the solution from variables { BoutReal* xdata = nullptr; - int ierr = VecGetArray(snes_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArray(snes_x, &xdata)); save_vars(xdata); - ierr = VecRestoreArray(snes_x, &xdata); - CHKERRQ(ierr); + + if (asinh_vars) { + // Evolving asinh(vars) + PetscInt size; + PetscCall(VecGetLocalSize(snes_x, &size)); + for (PetscInt i = 0; i != size; ++i) { + xdata[i] = std::asinh(xdata[i] / asinh_scale); + } + } + + PetscCall(VecRestoreArray(snes_x, &xdata)); + } + + int saved_jacobian_lag = 0; + if (scale_vars) { + PetscCall(rescale(saved_jacobian_lag)); } - for (int s = 0; s < getNumberOutputSteps(); s++) { - BoutReal target = simtime + getOutputTimestep(); + // Initialise residuals + local_residual = 0.0; + local_residual_2d = 0.0; + global_residual = 0.0; + PetscCall(updateResiduals(snes_x)); + if (diagnose) { + output.write("\n Residual: {}\n", global_residual); + } + + // If saving solver residuals, need to get initial value. + if (diagnose) { + snes_function(snes_x, snes_f, false); + } + + BoutReal target = simtime; + recent_failure_rate = 0.0; + int steps_since_rescale = 0; + BoutReal change_since_rescale = 0.; + for (int s = 1; s <= getNumberOutputSteps(); s++) { + target += getOutputTimestep(); bool looping = true; int snes_failures = 0; // Count SNES convergence failures - int saved_jacobian_lag = 0; - int loop_count = 0; - do { - if (scale_vars) { - // Individual variable scaling - // Note: If variables are rescaled then the Jacobian columns - // need to be scaled or recalculated - - if (loop_count % 100 == 0) { - // Rescale state (snes_x) so that all quantities are around 1 - // If quantities are near zero then RTOL is used - int istart, iend; - VecGetOwnershipRange(snes_x, &istart, &iend); - - // Take ownership of snes_x and var_scaling_factors data - PetscScalar* snes_x_data = nullptr; - ierr = VecGetArray(snes_x, &snes_x_data); - CHKERRQ(ierr); - PetscScalar* x1_data; - ierr = VecGetArray(x1, &x1_data); - CHKERRQ(ierr); - PetscScalar* var_scaling_factors_data; - ierr = VecGetArray(var_scaling_factors, &var_scaling_factors_data); - CHKERRQ(ierr); - - // Normalise each value in the state - // Limit normalisation so scaling factor is never smaller than rtol - for (int i = 0; i < iend - istart; ++i) { - const PetscScalar norm = - BOUTMAX(std::abs(snes_x_data[i]), rtol / var_scaling_factors_data[i]); - snes_x_data[i] /= norm; - x1_data[i] /= norm; // Update history for predictor - var_scaling_factors_data[i] *= norm; - } - - // Restore vector underlying data - ierr = VecRestoreArray(var_scaling_factors, &var_scaling_factors_data); - CHKERRQ(ierr); - ierr = VecRestoreArray(x1, &x1_data); - CHKERRQ(ierr); - ierr = VecRestoreArray(snes_x, &snes_x_data); - CHKERRQ(ierr); - if (diagnose) { - // Print maximum and minimum scaling factors - PetscReal max_scale, min_scale; - VecMax(var_scaling_factors, nullptr, &max_scale); - VecMin(var_scaling_factors, nullptr, &min_scale); - output.write("Var scaling: {} -> {}\n", min_scale, max_scale); - } + const BoutReal start_global_residual = global_residual; + do { + if ((output_trigger == BoutSnesOutput::fixed_time_interval && (simtime >= target)) + || (output_trigger == BoutSnesOutput::residual_ratio + && (global_residual <= start_global_residual * output_residual_ratio))) { + break; // Could happen if step over multiple outputs + } - // Force recalculation of the Jacobian - SNESGetLagJacobian(snes, &saved_jacobian_lag); - SNESSetLagJacobian(snes, 1); - } + if (scale_vars + and (change_since_rescale > rescale_threshold + or steps_since_rescale == rescale_period)) { + PetscCall(rescale(saved_jacobian_lag)); + change_since_rescale = 0.; + steps_since_rescale = 0; } - ++loop_count; // Copy the state (snes_x) into initial values (x0) VecCopy(snes_x, x0); + if (diagnose) { + VecCopy(snes_f, f0); + } + + if (equation_form == BoutSnesEquationForm::pseudo_transient) { + // Pseudo-Transient Continuation + // Each evolving quantity may have its own timestep + // Set timestep and dt scalars to the minimum of dt_vec - if (timestep < dt_min_reset) { - // Hit the minimum timestep, probably through repeated failures + PetscCall(VecMin(dt_vec, nullptr, ×tep)); + dt = timestep; - if (saved_jacobian_lag != 0) { - // Already tried this and it didn't work - throw BoutException("Solver failed after many attempts"); + if (output_trigger == BoutSnesOutput::fixed_time_interval + && simtime + timestep >= target) { + looping = false; } + } else { + // Timestepping - // Try resetting the preconditioner, turn off predictor, and use a large timestep - SNESGetLagJacobian(snes, &saved_jacobian_lag); - SNESSetLagJacobian(snes, 1); - timestep = getOutputTimestep(); - predictor = false; // Predictor can cause problems in near steady-state. - } + if (timestep < dt_min_reset) { + // Hit the minimum timestep, probably through repeated failures - // Set the timestep - dt = timestep; - looping = true; - if (simtime + dt >= target) { - // Ensure that the timestep goes to the next output time and then stops - looping = false; - dt = target - simtime; - } + if (saved_jacobian_lag != 0) { + // Already tried this and it didn't work + throw BoutException("Solver failed after many attempts"); + } - if (predictor and (time1 > 0.0)) { - // Use (time1, x1) and (simtime, x0) to make prediction - // snes_x <- x0 + (dt / (simtime - time1)) * (x0 - x1) - // snes_x <- -β * x1 + (1 + β) * snes_x - BoutReal beta = dt / (simtime - time1); - VecAXPBY(snes_x, -beta, (1. + beta), x1); + // Try resetting the preconditioner, turn off predictor, and use a large timestep + SNESGetLagJacobian(snes, &saved_jacobian_lag); + SNESSetLagJacobian(snes, 1); + timestep = getOutputTimestep(); + predictor = false; // Predictor can cause problems in near steady-state. + } + + // Set the timestep + dt = timestep; + if (output_trigger == BoutSnesOutput::fixed_time_interval + && simtime + dt >= target) { + // Note: When the timestep is changed the preconditioner needs to be updated + // => Step over the output time and interpolate if not matrix free + + if (matrix_free) { + // Ensure that the timestep goes to the next output time and then stops. + // This avoids the need to interpolate + dt = target - simtime; + } + looping = false; + } + + if (predictor and (time1 > 0.0)) { + // Use (time1, x1) and (simtime, x0) to make prediction + // snes_x <- x0 + (dt / (simtime - time1)) * (x0 - x1) + // snes_x <- -β * x1 + (1 + β) * snes_x + const BoutReal beta = dt / (simtime - time1); + VecAXPBY(snes_x, -beta, (1. + beta), x1); + } + + SNESSetLagJacobian(snes, lag_jacobian); } // Run the solver @@ -869,10 +799,17 @@ int SNESSolver::run() { int lin_its; SNESGetLinearSolveIterations(snes, &lin_its); - if ((ierr != 0) or (reason < 0)) { + // Rolling average of recent failures + recent_failure_rate *= 1. - last_failure_weight; + + if ((ierr != PETSC_SUCCESS) or (reason < 0)) { // Diverged or SNES failed - if (diagnose_failures) { + recent_failure_rate += last_failure_weight; + + ++snes_failures; + + if (diagnose_failures or (snes_failures == max_snes_failures)) { // Print diagnostics to help identify source of the problem output.write("\n======== SNES failed =========\n"); @@ -891,10 +828,29 @@ int SNESSolver::run() { } } - ++snes_failures; + if (snes_failures == max_snes_failures) { + output.write("Too many SNES failures ({}). Aborting.", snes_failures); + return 1; + } - // Try a smaller timestep - timestep /= 2.0; + if (equation_form == BoutSnesEquationForm::pseudo_transient) { + if (snes_failures == max_snes_failures - 1) { + // Last chance. Set to uniform smallest timestep + PetscCall(VecSet(dt_vec, dt_min_reset)); + + } else if (snes_failures == 5) { + // Set uniform timestep + PetscCall(VecSet(dt_vec, timestep)); + } else { + // Global scaling of timesteps + // Note: A better strategy might be to reduce timesteps + // in problematic cells. + PetscCall(VecScale(dt_vec, timestep_factor_on_failure)); + } + } else { + // Try a smaller timestep + timestep *= timestep_factor_on_failure; + } // Restore state VecCopy(x0, snes_x); @@ -902,17 +858,14 @@ int SNESSolver::run() { if (jacobian_pruned and (snes_failures > 2) and (4 * lin_its > 3 * maxl)) { // Taking 3/4 of maximum linear iterations on average per linear step // May indicate a preconditioner problem. - // Restore pruned non-zero elements if (diagnose) { output.write("\nRestoring Jacobian\n"); } - ierr = MatCopy(Jfd_original, Jfd, DIFFERENT_NONZERO_PATTERN); - CHKERRQ(ierr); - // The non-zero pattern has changed, so update coloring - updateColoring(); - jacobian_pruned = false; // Reset flag. Will be set after pruning. + PetscCall(FDJrestoreFromPruning()); } + if (saved_jacobian_lag == 0) { + // This triggers a Jacobian recalculation SNESGetLagJacobian(snes, &saved_jacobian_lag); SNESSetLagJacobian(snes, 1); } @@ -946,45 +899,65 @@ int SNESSolver::run() { if (nl_its == 0) { // This can occur even with SNESSetForceIteration - // Results in simulation state freezing and rapidly going to the end + // Results in simulation state freezing and rapidly going + // to the end if (scale_vars) { // scaled_x <- snes_x * var_scaling_factors - ierr = VecPointwiseMult(scaled_x, snes_x, var_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecPointwiseMult(scaled_x, snes_x, var_scaling_factors)); const BoutReal* xdata = nullptr; - ierr = VecGetArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(scaled_x, &xdata)); load_vars(const_cast(xdata)); - ierr = VecRestoreArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); } else { const BoutReal* xdata = nullptr; - ierr = VecGetArrayRead(snes_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(snes_x, &xdata)); load_vars(const_cast(xdata)); - ierr = VecRestoreArrayRead(snes_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArrayRead(snes_x, &xdata)); + } + + try { + run_rhs(simtime); + } catch (BoutException& e) { + output_error.write("ERROR: BoutException thrown: {}\n", e.what()); + // Abort simulation. There is no way to recover and + // synchronise unless all processors throw exceptions + // together + BoutComm::abort(1); } - run_rhs(simtime); // Copy derivatives back - { + if (diagnose) { + BoutReal* ddata = nullptr; + PetscCall(VecGetArray(deriv, &ddata)); + save_derivs(ddata); + PetscCall(VecRestoreArray(deriv, &ddata)); + if (scale_vars) { + PetscCall(VecPointwiseDivide(deriv, deriv, var_scaling_factors)); + } + // Forward Euler + VecAXPY(snes_x, dt, deriv); + } else { BoutReal* fdata = nullptr; - ierr = VecGetArray(snes_f, &fdata); - CHKERRQ(ierr); + PetscCall(VecGetArray(snes_f, &fdata)); save_derivs(fdata); - ierr = VecRestoreArray(snes_f, &fdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(snes_f, &fdata)); + if (scale_vars) { + PetscCall(VecPointwiseDivide(snes_f, snes_f, var_scaling_factors)); + } + // Forward Euler + VecAXPY(snes_x, dt, snes_f); } - - // Forward Euler - VecAXPY(snes_x, dt, snes_f); } simtime += dt; + // Update local and global residuals + PetscCall(updateResiduals(snes_x)); + change_since_rescale += global_residual * dt; + ++steps_since_rescale; + if (diagnose) { // Gather and print diagnostic information @@ -993,94 +966,121 @@ int SNESSolver::run() { simtime, timestep, nl_its, lin_its, static_cast(reason)); if (snes_failures > 0) { output.write(", SNES failures: {}", snes_failures); - snes_failures = 0; } output.write("\n"); + // Print residual on separate line, so post-processing isn't affected + output.write(" Residual: {}\n", global_residual); } -#if PETSC_VERSION_GE(3, 20, 0) // MatFilter and MatEliminateZeros(Mat, bool) require PETSc >= 3.20 if (jacobian_recalculated and prune_jacobian) { jacobian_recalculated = false; // Reset flag - // Remove small elements from the Jacobian and recompute the coloring - // Only do this if there are a significant number of small elements. - int small_elements = 0; - int total_elements = 0; - - // Get index of rows owned by this processor - int rstart, rend; - MatGetOwnershipRange(Jfd, &rstart, &rend); - - PetscInt ncols; - const PetscScalar* vals; - for (int row = rstart; row < rend; row++) { - MatGetRow(Jfd, row, &ncols, nullptr, &vals); - for (int col = 0; col < ncols; col++) { - if (std::abs(vals[col]) < prune_abstol) { - ++small_elements; - } - ++total_elements; - } - MatRestoreRow(Jfd, row, &ncols, nullptr, &vals); - } + FDJpruneJacobian(); + } - if (small_elements > prune_fraction * total_elements) { - if (diagnose) { - output.write("\nPruning Jacobian elements: {} / {}\n", small_elements, - total_elements); - } + if (equation_form == BoutSnesEquationForm::pseudo_transient) { + // Adjust pseudo_alpha to globally scale timesteps + pseudo_alpha = + std::max({updateGlobalTimestep(pseudo_alpha, nl_its, recent_failure_rate, + max_timestep * atol * 100), + pseudo_alpha_minimum}); - // Prune Jacobian, keeping diagonal elements - ierr = MatFilter(Jfd, prune_abstol, PETSC_TRUE, PETSC_TRUE); + // Adjust local timesteps + PetscCall(updatePseudoTimestepping()); - // Update the coloring from Jfd matrix - updateColoring(); + } else { + // Adjust timestep + timestep = + updateGlobalTimestep(timestep, nl_its, recent_failure_rate, max_timestep); + } - // Mark the Jacobian as pruned. This is so that it is only restored if pruned. - jacobian_pruned = true; + if (static_cast(lin_its) / nl_its > 0.5 * maxl) { + // Recompute Jacobian if number of linear iterations is too high + if (saved_jacobian_lag == 0) { + SNESGetLagJacobian(snes, &saved_jacobian_lag); + SNESSetLagJacobian(snes, 1); } } -#endif // PETSC_VERSION_GE(3,20,0) - if (looping) { - if (nl_its <= lower_its) { - // Increase timestep slightly - timestep *= 1.1; + snes_failures = 0; + } while (looping); - if (timestep > max_timestep) { - timestep = max_timestep; - } - } else if (nl_its >= upper_its) { - // Reduce timestep slightly - timestep *= 0.9; - } + BoutReal output_time = simtime; + if (output_trigger == BoutSnesOutput::fixed_time_interval && !matrix_free) { + ASSERT2(simtime >= target); + ASSERT2(simtime - dt <= target); + // Stepped over output timestep => Interpolate + // snes_x is the solution at t = simtime + // x0 is the solution at t = simtime - dt + // Calculate output_x at t = target + VecCopy(snes_x, output_x); + + // Note: If simtime = target then alpha = 0 + // and output_x = snes_x + const BoutReal alpha = (simtime - target) / dt; + + // output_x <- alpha * x0 + (1 - alpha) * output_x + VecAXPBY(output_x, alpha, 1. - alpha, x0); + + if (diagnose) { + VecCopy(snes_f, output_f); + VecAXPBY(output_f, alpha, 1 - alpha, f0); } - } while (looping); + + output_time = target; + } else { + // Timestep was adjusted to hit target output time + output_x = snes_x; + if (diagnose) { + output_f = snes_f; + } + } // Put the result into variables if (scale_vars) { - // scaled_x <- snes_x * var_scaling_factors - int ierr = VecPointwiseMult(scaled_x, snes_x, var_scaling_factors); - CHKERRQ(ierr); - - const BoutReal* xdata = nullptr; - ierr = VecGetArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); - load_vars(const_cast(xdata)); - ierr = VecRestoreArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); + // scaled_x <- output_x * var_scaling_factors + PetscCall(VecPointwiseMult(scaled_x, output_x, var_scaling_factors)); + } else if (asinh_vars) { + PetscCall(VecCopy(output_x, scaled_x)); } else { - const BoutReal* xdata = nullptr; - int ierr = VecGetArrayRead(snes_x, &xdata); - CHKERRQ(ierr); - load_vars(const_cast(xdata)); - ierr = VecRestoreArrayRead(snes_x, &xdata); - CHKERRQ(ierr); + scaled_x = output_x; + } + + if (asinh_vars) { + PetscInt size; + PetscCall(VecGetLocalSize(scaled_x, &size)); + + BoutReal* scaled_data = nullptr; + PetscCall(VecGetArray(scaled_x, &scaled_data)); + for (PetscInt i = 0; i != size; ++i) { + scaled_data[i] = asinh_scale * std::sinh(scaled_data[i]); + } + PetscCall(VecRestoreArray(scaled_x, &scaled_data)); + } + + const BoutReal* xdata = nullptr; + PetscCall(VecGetArrayRead(scaled_x, &xdata)); + load_vars(const_cast(xdata)); + PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); + + try { + run_rhs(output_time); // Run RHS to calculate auxilliary variables + } catch (BoutException& e) { + output_error.write("ERROR: BoutException thrown: {}\n", e.what()); + // Abort simulation. There is no way to recover unless + // all processors throw an exception at the same point. + BoutComm::abort(1); } - run_rhs(simtime); // Run RHS to calculate auxilliary variables - if (call_monitors(simtime, s, getNumberOutputSteps()) != 0) { + if (diagnose) { + BoutReal* resid_data = nullptr; + PetscCall(VecGetArray(output_f, &resid_data)); + loop_vars(resid_2d, resid_3d, resid_data, SOLVER_VAR_OP::LOAD); + PetscCall(VecRestoreArray(output_f, &resid_data)); + } + + if (call_monitors(output_time, s, getNumberOutputSteps()) != 0) { break; // User signalled to quit } } @@ -1088,31 +1088,372 @@ int SNESSolver::run() { return 0; } -// f = rhs -PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { +BoutReal SNESSolver::updateGlobalTimestep(BoutReal timestep, int nl_its, + BoutReal recent_failure_rate, BoutReal max_dt) { + // Note: The preconditioner depends on the timestep, + // so if it is not recalculated the it will be less + // effective. + + switch (timestep_control) { + case BoutSnesTimestep::pid_nonlinear_its: + // Changing the timestep using a PID controller. + return pid(timestep, nl_its, max_dt); + + case BoutSnesTimestep::threshold_nonlinear_its: + // Consider changing the timestep, based on thresholds in NL iterations + if ((nl_its <= lower_its) && (timestep < max_timestep) + && (recent_failure_rate < 0.5)) { + // Increase timestep slightly + timestep *= timestep_factor_on_lower_its; + return std::min(timestep, max_timestep); + } + + if (nl_its >= upper_its) { + // Reduce timestep slightly + return timestep * timestep_factor_on_upper_its; + } + return timestep; // No change + + case BoutSnesTimestep::residual_ratio: + // Use ratio of previous and current global residual + // Intended to be the same as https://petsc.org/release/manualpages/TS/TSPSEUDO/ + // (Note the PETSc manual has the expression for dt_n upside down) + + return std::min({timestep_factor * timestep * global_residual_prev / global_residual, + max_timestep}); + + case BoutSnesTimestep::fixed: + break; + } + return timestep; // No change +} + +PetscErrorCode SNESSolver::updateResiduals(Vec x) { + // Push residuals to previous residuals + // Copying so that data is not shared + local_residual_prev = copy(local_residual); + local_residual_2d_prev = copy(local_residual_2d); + global_residual_prev = global_residual; + + const BoutReal* current_residual = nullptr; + if (diagnose) { + // Call RHS function to get time derivatives + PetscCall(rhs_function(x, deriv, false)); + + // Reading the residual vectors + PetscCall(VecGetArrayRead(deriv, ¤t_residual)); + } else { + // Call RHS function to get time derivatives + PetscCall(rhs_function(x, snes_f, false)); + + // Reading the residual vectors + PetscCall(VecGetArrayRead(snes_f, ¤t_residual)); + } + + // Note: The ordering of quantities in the PETSc vectors + // depends on the Solver::loop_vars function + Mesh* mesh = bout::globals::mesh; + int idx = 0; // Index into PETSc Vecs + + // Boundary cells + for (const auto& i2d : mesh->getRegion2D("RGN_BNDRY")) { + // Field2D quantities evolved together + BoutReal residual = 0.0; + int count = 0; + + for (const auto& f : f2d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + residual += SQ(current_residual[idx++]); + ++count; + } + if (count > 0) { + local_residual_2d[i2d] = sqrt(residual / count); + } + + // Field3D quantities evolved together within a cell + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { + count = 0; + residual = 0.0; + for (const auto& f : f3d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + residual += SQ(current_residual[idx++]); + ++count; + } + if (count > 0) { + auto i3d = mesh->ind2Dto3D(i2d, jz); + local_residual[i3d] = sqrt(residual / count); + } + } + } + + // Bulk of domain. + // These loops don't check the boundary flags + for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { + // Field2D quantities evolved together + if (!f2d.empty()) { + BoutReal residual = 0.0; + for (std::size_t i = 0; i != f2d.size(); ++i) { + residual += SQ(current_residual[idx++]); + } + local_residual_2d[i2d] = sqrt(residual / static_cast(f2d.size())); + } + + // Field3D quantities evolved together within a cell + if (!f3d.empty()) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { + auto i3d = mesh->ind2Dto3D(i2d, jz); + + BoutReal residual = 0.0; + for (std::size_t i = 0; i != f3d.size(); ++i) { + residual += SQ(current_residual[idx++]); + } + local_residual[i3d] = sqrt(residual / static_cast(f3d.size())); + } + } + } + + if (diagnose) { + // Restore Vec data arrays + PetscCall(VecRestoreArrayRead(deriv, ¤t_residual)); + } else { + PetscCall(VecRestoreArrayRead(snes_f, ¤t_residual)); + } + + // Global residual metric (RMS) + global_residual = std::sqrt(mean(SQ(local_residual), true)); + + return PETSC_SUCCESS; +} + +PetscErrorCode SNESSolver::initPseudoTimestepping() { + // Storage for per-variable timestep + PetscCall(VecDuplicate(snes_x, &dt_vec)); + // Starting timestep + PetscCall(VecSet(dt_vec, timestep)); + + // Diagnostic outputs + pseudo_timestep = timestep; + + return PETSC_SUCCESS; +} + +PetscErrorCode SNESSolver::updatePseudoTimestepping() { + // Use a per-cell timestep so that e.g density and pressure + // evolve in a way consistent with the equation of state. + + // Modifying the dt_vec values + BoutReal* dt_data = nullptr; + PetscCall(VecGetArray(dt_vec, &dt_data)); + + // Note: The ordering of quantities in the PETSc vectors + // depends on the Solver::loop_vars function + Mesh* mesh = bout::globals::mesh; + int idx = 0; // Index into PETSc Vecs + + // Boundary cells + for (const auto& i2d : mesh->getRegion2D("RGN_BNDRY")) { + // Field2D quantities evolved together + int count = 0; + for (const auto& f : f2d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + ++count; + } + if (count > 0) { + // Adjust timestep for these quantities + const BoutReal new_timestep = updatePseudoTimestep( + dt_data[idx], local_residual_2d_prev[i2d], local_residual_2d[i2d]); + for (int i = 0; i != count; ++i) { + dt_data[idx++] = new_timestep; + } + } + + // Field3D quantities evolved together within a cell + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { + count = 0; + for (const auto& f : f3d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + ++count; + } + if (count > 0) { + auto i3d = mesh->ind2Dto3D(i2d, jz); + const BoutReal new_timestep = updatePseudoTimestep( + dt_data[idx], local_residual_prev[i3d], local_residual[i3d]); + + for (int i = 0; i != count; ++i) { + dt_data[idx++] = new_timestep; + } + } + } + } + + // Bulk of domain. + // These loops don't check the boundary flags + for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { + // Field2D quantities evolved together + if (!f2d.empty()) { + // Adjust timestep for these quantities + const BoutReal new_timestep = updatePseudoTimestep( + dt_data[idx], local_residual_2d_prev[i2d], local_residual_2d[i2d]); + for (std::size_t i = 0; i != f2d.size(); ++i) { + dt_data[idx++] = new_timestep; + } + } + + // Field3D quantities evolved together within a cell + if (!f3d.empty()) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { + auto i3d = mesh->ind2Dto3D(i2d, jz); + + BoutReal new_timestep = updatePseudoTimestep( + dt_data[idx], local_residual_prev[i3d], local_residual[i3d]); + + // Compare to neighbors + BoutReal min_neighboring_dt = max_timestep; + if (i3d.x() != 0) { + min_neighboring_dt = std::min(min_neighboring_dt, pseudo_timestep[i3d.xm()]); + } + if (i3d.x() != mesh->LocalNx - 1) { + min_neighboring_dt = std::min(min_neighboring_dt, pseudo_timestep[i3d.xp()]); + } + if (i3d.y() != 0) { + min_neighboring_dt = std::min(min_neighboring_dt, pseudo_timestep[i3d.ym()]); + } + if (i3d.x() != mesh->LocalNy - 1) { + min_neighboring_dt = std::min(min_neighboring_dt, pseudo_timestep[i3d.yp()]); + } + + // Limit ratio of timestep between neighboring cells + new_timestep = std::min(new_timestep, pseudo_max_ratio * min_neighboring_dt); + + pseudo_timestep[i3d] = new_timestep; + + for (std::size_t i = 0; i != f3d.size(); ++i) { + dt_data[idx++] = new_timestep; + } + } + } + } + + // Restore Vec data arrays + PetscCall(VecRestoreArray(dt_vec, &dt_data)); + + // Need timesteps on neighboring processors + // to limit ratio + mesh->communicate(pseudo_timestep); + // Neumann boundary so timestep isn't pinned at boundaries + pseudo_timestep.applyBoundary("neumann"); + + return PETSC_SUCCESS; +} + +/// Timestep inversely proportional to the residual, clipped to avoid +/// rapid changes in timestep. +BoutReal SNESSolver::updatePseudoTimestep_inverse_residual(BoutReal previous_timestep, + BoutReal current_residual) { + return std::max( + {std::min({std::max({pseudo_alpha / current_residual, previous_timestep / 1.5}), + 1.5 * previous_timestep, max_timestep}), + dt_min_reset}); +} + +// Strategy based on history of residuals +BoutReal SNESSolver::updatePseudoTimestep_history_based(BoutReal previous_timestep, + BoutReal previous_residual, + BoutReal current_residual) const { + const BoutReal converged_threshold = 10 * atol; + const BoutReal transition_threshold = 100 * atol; + + if (current_residual < converged_threshold) { + return max_timestep; + } + + // Smoothly transition from SER updates to frozen timestep + if (current_residual < transition_threshold) { + const BoutReal reduction_ratio = current_residual / previous_residual; + + if (reduction_ratio < 0.8) { + return std::min(0.5 * (pseudo_growth_factor + 1.) * previous_timestep, + max_timestep); + } + if (reduction_ratio > 1.2) { + return std::max(0.5 * (pseudo_reduction_factor + 1) * previous_timestep, + dt_min_reset); + } + // Leave unchanged if residual changes are small + return previous_timestep; + } + + if (current_residual <= previous_residual) { + // Success + return std::min(pseudo_growth_factor * previous_timestep, max_timestep); + } + // Failed + // Consider rejecting the step + return std::max(pseudo_reduction_factor * previous_timestep, dt_min_reset); +} + +/// Switched Evolution Relaxation (SER) +BoutReal SNESSolver::updatePseudoTimestep(BoutReal previous_timestep, + BoutReal previous_residual, + BoutReal current_residual) { + switch (pseudo_strategy) { + case BoutPTCStrategy::inverse_residual: + return updatePseudoTimestep_inverse_residual(previous_timestep, current_residual); + + case BoutPTCStrategy::history_based: + return updatePseudoTimestep_history_based(previous_timestep, previous_residual, + current_residual); + + case BoutPTCStrategy::hybrid: + // A hybrid strategy may be most effective, in which the timestep is + // inversely proportional to residual initially, or when residuals are large, + // and then the method transitions to being history-based + if (current_residual > 1000. * atol) { + return updatePseudoTimestep_inverse_residual(previous_timestep, current_residual); + } + return updatePseudoTimestep_history_based(previous_timestep, previous_residual, + current_residual); + }; + throw BoutException("SNESSolver::updatePseudoTimestep invalid BoutPTCStrategy"); +} + +PetscErrorCode SNESSolver::rhs_function(Vec x, Vec f, bool linear) { // Get data from PETSc into BOUT++ fields if (scale_vars) { // scaled_x <- x * var_scaling_factors - int ierr = VecPointwiseMult(scaled_x, x, var_scaling_factors); - CHKERRQ(ierr); - - const BoutReal* xdata = nullptr; - ierr = VecGetArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); - load_vars(const_cast( - xdata)); // const_cast needed due to load_vars API. Not writing to xdata. - ierr = VecRestoreArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecPointwiseMult(scaled_x, x, var_scaling_factors)); + } else if (asinh_vars) { + PetscCall(VecCopy(x, scaled_x)); } else { - const BoutReal* xdata = nullptr; - int ierr = VecGetArrayRead(x, &xdata); - CHKERRQ(ierr); - load_vars(const_cast( - xdata)); // const_cast needed due to load_vars API. Not writing to xdata. - ierr = VecRestoreArrayRead(x, &xdata); - CHKERRQ(ierr); + scaled_x = x; } + if (asinh_vars) { + PetscInt size; + PetscCall(VecGetLocalSize(scaled_x, &size)); + + BoutReal* scaled_data = nullptr; + PetscCall(VecGetArray(scaled_x, &scaled_data)); + for (PetscInt i = 0; i != size; ++i) { + scaled_data[i] = asinh_scale * std::sinh(scaled_data[i]); + } + PetscCall(VecRestoreArray(scaled_x, &scaled_data)); + } + + const BoutReal* xdata = nullptr; + PetscCall(VecGetArrayRead(scaled_x, &xdata)); + // const_cast needed due to load_vars API. Not writing to xdata. + load_vars(const_cast(xdata)); + PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); + try { // Call RHS function run_rhs(simtime + dt, linear); @@ -1120,28 +1461,53 @@ PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { // Simulation might fail, e.g. negative densities // if timestep too large output_warn.write("WARNING: BoutException thrown: {}\n", e.what()); + // Abort simulation. Unless all processors threw an exception + // at the same point, there is no way to synchronise again. + BoutComm::abort(2); + } + // Copy derivatives back + BoutReal* fdata = nullptr; + PetscCall(VecGetArray(f, &fdata)); + save_derivs(fdata); + + if (asinh_vars) { + // Modify time-derivatives for asinh(var) using chain rule + // Evolving u = asinh(var / scale) + // + // du/dt = dvar/dt * du/dvar + // + // du/var = 1 / sqrt(var^2 + scale^2) + PetscInt size; + PetscCall(VecGetLocalSize(f, &size)); + const BoutReal* scaled_data = nullptr; + PetscCall(VecGetArrayRead(scaled_x, &scaled_data)); + for (PetscInt i = 0; i != size; ++i) { + fdata[i] /= std::sqrt(SQ(scaled_data[i]) + SQ(asinh_scale)); + } + PetscCall(VecRestoreArrayRead(scaled_x, &scaled_data)); + } + + PetscCall(VecRestoreArray(f, &fdata)); + + if (scale_vars) { + PetscCall(VecPointwiseDivide(f, f, var_scaling_factors)); + } + return PETSC_SUCCESS; +} + +// Result in f depends on equation_form +PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { + + // Call the RHS function + if (rhs_function(x, f, linear) != PETSC_SUCCESS) { // Tell SNES that the input was out of domain SNESSetFunctionDomainError(snes); // Note: Returning non-zero error here leaves vectors in locked state return 0; } - // Copy derivatives back - BoutReal* fdata = nullptr; - int ierr = VecGetArray(f, &fdata); - CHKERRQ(ierr); - save_derivs(fdata); - ierr = VecRestoreArray(f, &fdata); - CHKERRQ(ierr); - switch (equation_form) { - case BoutSnesEquationForm::pseudo_transient: { - // Pseudo-transient timestepping (as in UEDGE) - // f <- f - x/Δt - VecAXPY(f, -1. / dt, x); - break; - } case BoutSnesEquationForm::rearranged_backward_euler: { // Rearranged Backward Euler // f = (x0 - x)/Δt + f @@ -1150,6 +1516,15 @@ PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { VecAXPY(f, -1. / dt, delta_x); // f <- f - delta_x / dt break; } + case BoutSnesEquationForm::pseudo_transient: { + // Pseudo-transient timestepping. Same as Rearranged Backward Euler + // except that Δt is a vector + // f = (x0 - x)/Δt + f + VecWAXPY(delta_x, -1.0, x0, x); + VecPointwiseDivide(delta_x, delta_x, dt_vec); // delta_x /= dt + VecAXPY(f, -1., delta_x); // f <- f - delta_x + break; + } case BoutSnesEquationForm::backward_euler: { // Backward Euler // Set f = x - x0 - Δt*f @@ -1168,11 +1543,10 @@ PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { if (scale_rhs) { // f <- f * rhs_scaling_factors - ierr = VecPointwiseMult(f, f, rhs_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecPointwiseMult(f, f, rhs_scaling_factors)); } - return 0; + return PETSC_SUCCESS; } /* @@ -1184,36 +1558,28 @@ PetscErrorCode SNESSolver::precon(Vec x, Vec f) { throw BoutException("No user preconditioner"); } - int ierr; - // Get data from PETSc into BOUT++ fields Vec solution; - SNESGetSolution(snes, &solution); + PetscCall(SNESGetSolution(snes, &solution)); BoutReal* soldata; - ierr = VecGetArray(solution, &soldata); - CHKERRQ(ierr); + PetscCall(VecGetArray(solution, &soldata)); load_vars(soldata); - ierr = VecRestoreArray(solution, &soldata); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(solution, &soldata)); // Load vector to be inverted into ddt() variables const BoutReal* xdata; - ierr = VecGetArrayRead(x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(x, &xdata)); load_derivs(const_cast(xdata)); // Note: load_derivs does not modify data - ierr = VecRestoreArrayRead(x, &xdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArrayRead(x, &xdata)); // Run the preconditioner runPreconditioner(simtime + dt, dt, 0.0); // Save the solution from F_vars BoutReal* fdata; - ierr = VecGetArray(f, &fdata); - CHKERRQ(ierr); + PetscCall(VecGetArray(f, &fdata)); save_derivs(fdata); - ierr = VecRestoreArray(f, &fdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(f, &fdata)); return 0; } @@ -1222,11 +1588,9 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { jacobian_recalculated = true; if (!scale_rhs) { - return 0; // Not scaling the RHS values + return PETSC_SUCCESS; // Not scaling the RHS values } - int ierr; - // Get index of rows owned by this processor int rstart, rend; MatGetOwnershipRange(Jac_new, &rstart, &rend); @@ -1241,8 +1605,7 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { // Calculate the norm of each row of the Jacobian PetscScalar* row_inv_norm_data; - ierr = VecGetArray(jac_row_inv_norms, &row_inv_norm_data); - CHKERRQ(ierr); + PetscCall(VecGetArray(jac_row_inv_norms, &row_inv_norm_data)); PetscInt ncols; const PetscScalar* vals; @@ -1252,10 +1615,8 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { // Calculate a norm of this row of the Jacobian PetscScalar norm = 0.0; for (int col = 0; col < ncols; col++) { - PetscScalar absval = std::abs(vals[col]); - if (absval > norm) { - norm = absval; - } + const PetscScalar absval = std::abs(vals[col]); + norm = std::max({norm, absval}); // Can we identify small elements and remove them? // so we don't need to calculate them next time } @@ -1266,12 +1627,11 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { MatRestoreRow(Jac_new, row, &ncols, nullptr, &vals); } - ierr = VecRestoreArray(jac_row_inv_norms, &row_inv_norm_data); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(jac_row_inv_norms, &row_inv_norm_data)); // Modify the RHS scaling: factor = factor / norm - ierr = VecPointwiseMult(rhs_scaling_factors, rhs_scaling_factors, jac_row_inv_norms); - CHKERRQ(ierr); + PetscCall( + VecPointwiseMult(rhs_scaling_factors, rhs_scaling_factors, jac_row_inv_norms)); if (diagnose) { // Print maximum and minimum scaling factors @@ -1282,12 +1642,12 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { } // Scale the Jacobian rows by multiplying on the left by 1/norm - ierr = MatDiagonalScale(Jac_new, jac_row_inv_norms, nullptr); - CHKERRQ(ierr); + PetscCall(MatDiagonalScale(Jac_new, jac_row_inv_norms, nullptr)); - return 0; + return PETSC_SUCCESS; } +namespace { /// /// Input Parameters: /// snes - nonlinear solver object @@ -1298,8 +1658,8 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { /// Jac - Jacobian matrix (not altered in this routine) /// Jac_new - newly computed Jacobian matrix to use with preconditioner (generally the same as /// Jac) -static PetscErrorCode ComputeJacobianScaledColor(SNES snes, Vec x1, Mat Jac, Mat Jac_new, - void* ctx) { +PetscErrorCode ComputeJacobianScaledColor(SNES snes, Vec x1, Mat Jac, Mat Jac_new, + void* ctx) { PetscErrorCode err = SNESComputeJacobianDefaultColor(snes, x1, Jac, Jac_new, ctx); CHKERRQ(err); @@ -1316,34 +1676,70 @@ static PetscErrorCode ComputeJacobianScaledColor(SNES snes, Vec x1, Mat Jac, Mat // Call the SNESSolver function return fctx->scaleJacobian(Jac_new); } +} // namespace -void SNESSolver::updateColoring() { - // Re-calculate the coloring - MatColoring coloring = NULL; - MatColoringCreate(Jfd, &coloring); - MatColoringSetType(coloring, MATCOLORINGSL); - MatColoringSetFromOptions(coloring); - - // Calculate new index sets - ISColoring iscoloring = NULL; - MatColoringApply(coloring, &iscoloring); - MatColoringDestroy(&coloring); - - // Replace the old coloring with the new one - MatFDColoringDestroy(&fdcoloring); - MatFDColoringCreate(Jfd, iscoloring, &fdcoloring); - MatFDColoringSetFunction( - fdcoloring, reinterpret_cast(FormFunctionForColoring), this); - MatFDColoringSetFromOptions(fdcoloring); - MatFDColoringSetUp(Jfd, iscoloring, fdcoloring); - ISColoringDestroy(&iscoloring); - - // Replace the CTX pointer in SNES Jacobian - if (matrix_free_operator) { - // Use matrix-free calculation for operator, finite difference for preconditioner - SNESSetJacobian(snes, Jmf, Jfd, ComputeJacobianScaledColor, fdcoloring); - } else { - SNESSetJacobian(snes, Jfd, Jfd, ComputeJacobianScaledColor, fdcoloring); +BoutReal SNESSolver::pid(BoutReal timestep, int nl_its, BoutReal max_dt) { + + /* ---------- multiplicative PID factors ---------- */ + const BoutReal facP = std::pow(double(target_its) / double(nl_its), kP); + const BoutReal facI = std::pow(double(nl_its_prev) / double(nl_its), kI); + const BoutReal facD = std::pow(double(nl_its_prev) * double(nl_its_prev) + / double(nl_its) / double(nl_its_prev2), + kD); + + // clamp growth factor to avoid huge changes + BoutReal fac = std::clamp(facP * facI * facD, 0.2, 5.0); + + if (pid_consider_failures && (fac > 1.0)) { + // Reduce aggressiveness if recent steps have failed often + fac = pow(fac, std::max(0.3, 1.0 - 2.0 * recent_failure_rate)); + } + + /* ---------- update timestep and history ---------- */ + const BoutReal dt_new = std::min(timestep * fac, max_dt); + + nl_its_prev2 = nl_its_prev; + nl_its_prev = nl_its; + + return dt_new; +} + +void SNESSolver::outputVars(Options& output_options, bool save_repeat) { + // Call base class function + Solver::outputVars(output_options, save_repeat); + + if (!save_repeat) { + return; // Don't save diagnostics to restart files + } + + output_options["snes_local_residual"].assignRepeat(local_residual, "t", save_repeat, + "SNESSolver"); + output_options["snes_global_residual"].assignRepeat(global_residual, "t", save_repeat, + "SNESSolver"); + + if (equation_form == BoutSnesEquationForm::pseudo_transient) { + output_options["snes_pseudo_alpha"].assignRepeat(pseudo_alpha, "t", save_repeat, + "SNESSolver"); + output_options["snes_pseudo_timestep"].assignRepeat(pseudo_timestep, "t", save_repeat, + "SNESSolver"); + } + + if (diagnose) { + auto meta_2d_it = f2d.begin(); + for (const auto& f : resid_2d) { + const std::string name = "resid_" + meta_2d_it->name; + output_options[name].assignRepeat(f, "t", save_repeat, "SNES"); + output_options[name].attributes["description"] = meta_2d_it->description; + ++meta_2d_it; + } + + auto meta_3d_it = f3d.begin(); + for (const auto& f : resid_3d) { + const std::string name = "resid_" + meta_3d_it->name; + output_options[name].assignRepeat(f, "t", save_repeat, "SNES"); + output_options[name].attributes["description"] = meta_3d_it->description; + ++meta_3d_it; + } } } diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 17050ad775..6cdaac082c 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -4,7 +4,7 @@ * using PETSc for the SNES interface * ************************************************************************** - * Copyright 2015-2024 BOUT++ contributors + * Copyright 2015-2026 BOUT++ contributors * * Contact: Ben Dudson, dudson2@llnl.gov * @@ -35,10 +35,15 @@ class SNESSolver; +#include + #include "mpi.h" #include #include +#include +#include +#include #include #include @@ -52,12 +57,27 @@ RegisterSolver registersolverbeuler("beuler"); BOUT_ENUM_CLASS(BoutSnesEquationForm, pseudo_transient, rearranged_backward_euler, backward_euler, direct_newton); +BOUT_ENUM_CLASS(BoutPTCStrategy, + inverse_residual, ///< dt = pseudo_alpha / residual + history_based, ///< Grow/shrink dt based on residual decrease/increase + hybrid); ///< Combine inverse_residual and history_based strategies + +BOUT_ENUM_CLASS(BoutSnesTimestep, + pid_nonlinear_its, ///< PID controller on nonlinear iterations + threshold_nonlinear_its, ///< Use thresholds on nonlinear iterations + residual_ratio, ///< Use ratio of previous and current residual + fixed); ///< Fixed timestep (no adaptation) + +BOUT_ENUM_CLASS(BoutSnesOutput, + fixed_time_interval, ///< Output at fixed time intervals + residual_ratio); ///< When the residual is reduced by a given ratio + /// Uses PETSc's SNES interface to find a steady state solution to a /// nonlinear ODE by integrating in time with Backward Euler class SNESSolver : public Solver { public: explicit SNESSolver(Options* opts = nullptr); - ~SNESSolver() = default; + ~SNESSolver() override = default; int init() override; int run() override; @@ -68,6 +88,8 @@ public: /// /// f = (x - gamma*G(x)) - rhs /// + /// The form depends on equation_form + /// /// /// @param[in] x The state vector /// @param[out] f The vector for the result f(x) @@ -86,46 +108,140 @@ public: /// and update the internal RHS scaling factors /// This is called by SNESComputeJacobianScaledColor with the /// finite difference approximated Jacobian. - PetscErrorCode scaleJacobian(Mat B); + PetscErrorCode scaleJacobian(Mat Jac_new); + + /// Save diagnostics to output + void outputVars(Options& output_options, bool save_repeat = true) override; private: + PetscErrorCode FDJinitialise(); ///< Finite Difference Jacobian initialise + PetscErrorCode FDJpruneJacobian(); ///< Remove small elements from the Jacobian + PetscErrorCode FDJrestoreFromPruning(); ///< Restore Jacobian to original pattern + + /// Rescale state (snes_x) so that all quantities are around 1. If + /// quantities are near zero then RTOL is used. + PetscErrorCode rescale(int& saved_jacobian_lag); + + /// Call the physics model RHS function + /// + /// @param[in] x The state vector. Will be scaled if scale_vars=true + /// @param[out] f The vector for the result f(x) + /// @param[in] linear Specifies that the SNES solver is in a linear (KSP) inner loop + PetscErrorCode rhs_function(Vec x, Vec f, bool linear); + + BoutSnesOutput output_trigger; ///< Sets when outputs are written + + BoutReal output_residual_ratio; ///< Trigger an output when residual falls by this ratio + BoutReal timestep; ///< Internal timestep - BoutReal dt; ///< Current timestep used in snes_function + BoutReal dt{0.0}; ///< Current timestep used in snes_function. BoutReal dt_min_reset; ///< If dt falls below this, reset solve BoutReal max_timestep; ///< Maximum timestep + /// Form of the equation to solve + BoutSnesEquationForm equation_form; + std::string snes_type; BoutReal atol; ///< Absolute tolerance BoutReal rtol; ///< Relative tolerance BoutReal stol; ///< Convergence tolerance + int maxf; ///< Maximum number of function evaluations allowed in the solver (default: 10000) int maxits; ///< Maximum nonlinear iterations int lower_its, upper_its; ///< Limits on iterations for timestep adjustment + int max_snes_failures; ///< Maximum number of consecutive SNES failures before abort. + + BoutReal timestep_factor_on_failure; + BoutReal timestep_factor_on_upper_its; + BoutReal timestep_factor_on_lower_its; + + // Pseudo-Transient Continuation (PTC) variables + // These are used if equation_form = pseudo_transient + BoutPTCStrategy pseudo_strategy; ///< Strategy to use when setting timesteps + BoutReal pseudo_alpha; ///< dt = alpha / residual + BoutReal pseudo_alpha_minimum; ///< Minimum value of alpha + BoutReal pseudo_growth_factor; ///< Timestep increase 1.1 - 1.2 + BoutReal pseudo_reduction_factor; ///< Timestep decrease 0.5 + BoutReal pseudo_max_ratio; ///< Maximum timestep ratio between neighboring cells + Vec dt_vec; ///< Each quantity can have its own timestep + + /// Adjust the global timestep + BoutReal updateGlobalTimestep(BoutReal timestep, int nl_its, + BoutReal recent_failure_rate, BoutReal max_dt); + + /// Calculate per-cell and global residuals + /// given an input system state `x` + PetscErrorCode updateResiduals(Vec x); + Field3D local_residual{0.0}; ///< Residual of Field3D quantities in each cell + Field2D local_residual_2d{0.0}; ///< Residual of Field2D quantities in each cell + BoutReal global_residual{0.0}; ///< Global residual measure + Field3D local_residual_prev{0.0}; ///< Previous Field3D local residuals + Field2D local_residual_2d_prev{0.0}; ///< Previous Field2D local residuals + BoutReal global_residual_prev{0.0}; ///< Previous global residual + + /// Initialize the Pseudo-Transient Continuation method + PetscErrorCode initPseudoTimestepping(); + /// Update dt_vec based on residuals + PetscErrorCode updatePseudoTimestepping(); + /// Decide the next pseudo-timestep. Called by updatePseudoTimestepping + BoutReal updatePseudoTimestep(BoutReal previous_timestep, BoutReal previous_residual, + BoutReal current_residual); + BoutReal updatePseudoTimestep_inverse_residual(BoutReal previous_timestep, + BoutReal current_residual); + BoutReal updatePseudoTimestep_history_based(BoutReal previous_timestep, + BoutReal previous_residual, + BoutReal current_residual) const; + + Field3D pseudo_timestep; + + /// Timestep controller method + BoutSnesTimestep timestep_control; + + /// When using BoutSnesTimestep::residual_ratio + BoutReal timestep_factor; ///< Multiply timestep by this each step + + /// Target number of nonlinear iterations for the PID controller. + /// This can be non-integer to push timestep more aggressively + BoutReal target_its; + + BoutReal kP; ///< (0.6 - 0.8) Proportional parameter (main response to current step) + BoutReal kI; ///< (0.2 - 0.4) Integral parameter (smooths history of changes) + BoutReal kD; ///< (0.1 - 0.3) Derivative (dampens oscillation - optional) + bool pid_consider_failures; ///< Reduce timestep increases if recent solves have failed + BoutReal recent_failure_rate; ///< Rolling average of recent failure rate + BoutReal last_failure_weight; ///< 1 / number of recent solves + + BoutReal nl_its_prev; + BoutReal nl_its_prev2; + + BoutReal pid(BoutReal timestep, int nl_its, BoutReal max_dt); ///< Updates the timestep + bool diagnose; ///< Output additional diagnostics bool diagnose_failures; ///< Print diagnostics on SNES failures int nlocal; ///< Number of variables on local processor int neq; ///< Number of variables in total - /// Form of the equation to solve - BoutSnesEquationForm equation_form; - PetscLib lib; ///< Handles initialising, finalising PETSc Vec snes_f; ///< Used by SNES to store function - Vec snes_x; ///< Result of SNES - Vec x0; ///< Solution at start of current timestep - Vec delta_x; ///< Change in solution + Vec deriv; ///< Time derivative; only used if diagnose = true, otherwise will store in snes_f + Vec snes_x; ///< Result of SNES + Vec x0; ///< Solution at start of current timestep + Vec f0; ///< Residual at start of current timestep (only stored if diagnose = true) + Vec delta_x; ///< Change in solution + Vec output_x; ///< Solution to output. Used if interpolating. + Vec output_f; ///< Residual to output, if diagnose == true. Used if interpolating. bool predictor; ///< Use linear predictor? Vec x1; ///< Previous solution BoutReal time1{-1.0}; ///< Time of previous solution - SNES snes; ///< SNES context - Mat Jmf; ///< Matrix Free Jacobian - Mat Jfd; ///< Finite Difference Jacobian - MatFDColoring fdcoloring{nullptr}; ///< Matrix coloring context - ///< Jacobian evaluation + SNES snes; ///< SNES context + Mat Jmf; ///< Matrix Free Jacobian + Mat Jfd; ///< Finite Difference Jacobian (brute-force, when not using coloring) + PetscPreconditioner + petsc_preconditioner; ///< Coloring-based FD Jacobian + MatFDColoring bool use_precon; ///< Use preconditioner std::string ksp_type; ///< Linear solver type @@ -135,10 +251,11 @@ private: std::string pc_hypre_type; ///< Hypre preconditioner type std::string line_search_type; ///< Line search type - bool matrix_free; ///< Use matrix free Jacobian - bool matrix_free_operator; ///< Use matrix free Jacobian in the operator? - int lag_jacobian; ///< Re-use Jacobian - bool use_coloring; ///< Use matrix coloring + bool matrix_free; ///< Use matrix free Jacobian + bool matrix_free_operator; ///< Use matrix free Jacobian in the operator? + int lag_jacobian; ///< Re-use Jacobian + bool jacobian_persists; ///< Re-use Jacobian and preconditioner across nonlinear solves + bool use_coloring; ///< Use matrix coloring bool jacobian_recalculated; ///< Flag set when Jacobian is recalculated bool prune_jacobian; ///< Remove small elements in the Jacobian? @@ -146,15 +263,25 @@ private: BoutReal prune_fraction; ///< Prune if fraction of small elements is larger than this bool jacobian_pruned{false}; ///< Has the Jacobian been pruned? Mat Jfd_original; ///< Used to reset the Jacobian if over-pruned - void updateColoring(); ///< Updates the coloring using Jfd bool scale_rhs; ///< Scale time derivatives? Vec rhs_scaling_factors; ///< Factors to multiply RHS function Vec jac_row_inv_norms; ///< 1 / Norm of the rows of the Jacobian - bool scale_vars; ///< Scale individual variables? + bool scale_vars; ///< Scale individual variables? + int rescale_period; ///< How many time-steps before rescaling variables + BoutReal + rescale_threshold; //< How much change in the state there should be before rescaling Vec var_scaling_factors; ///< Factors to multiply variables when passing to user Vec scaled_x; ///< The values passed to the user RHS + + bool asinh_vars; ///< Evolve asinh(vars) to compress magnitudes while preserving signs + const BoutReal asinh_scale = 1e-5; // Scale below which asinh response becomes ~linear + + std::vector + resid_2d; ///< Storage for residuals of SNES solve, unpacked from snes_f + std::vector + resid_3d; ///< Storage for residuals of SNES solve, unpacked from snes_f }; #else diff --git a/src/solver/impls/split-rk/split-rk.cxx b/src/solver/impls/split-rk/split-rk.cxx index cd6bd1718c..e5fb0f6a8e 100644 --- a/src/solver/impls/split-rk/split-rk.cxx +++ b/src/solver/impls/split-rk/split-rk.cxx @@ -1,5 +1,21 @@ #include "split-rk.hxx" +#include "bout/array.hxx" +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" +#include "bout/field2d.hxx" +#include "bout/globals.hxx" +#include "bout/openmpwrap.hxx" +#include "bout/options.hxx" +#include "bout/output.hxx" +#include "bout/solver.hxx" +#include "bout/sys/gettext.hxx" +#include "bout/utils.hxx" + +#include +#include + SplitRK::SplitRK(Options* opts) : Solver(opts), nstages((*options)["nstages"] .doc("Number of stages in RKL step. Must be > 1") @@ -35,7 +51,6 @@ SplitRK::SplitRK(Options* opts) } int SplitRK::init() { - AUTO_TRACE(); Solver::init(); output.write(_("\n\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n")); @@ -73,15 +88,14 @@ int SplitRK::init() { ASSERT0(ninternal_steps > 0); timestep = getOutputTimestep() / ninternal_steps; - output.write(_("\tUsing a timestep {:e}\n"), timestep); + output.write(_f("\tUsing a timestep {:e}\n"), timestep); return 0; } int SplitRK::run() { - AUTO_TRACE(); - for (int step = 0; step < getNumberOutputSteps(); step++) { + for (int step = 1; step <= getNumberOutputSteps(); step++) { // Take an output step BoutReal target = simtime + getOutputTimestep(); diff --git a/src/solver/nvector.hxx b/src/solver/nvector.hxx new file mode 100644 index 0000000000..39aa52d3ca --- /dev/null +++ b/src/solver/nvector.hxx @@ -0,0 +1,282 @@ +/************************************************************************** + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov + * + * This file is part of BOUT++. + * + * BOUT++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * BOUT++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with BOUT++. If not, see . + * + **************************************************************************/ + +#ifndef BOUT_NVECTOR_H +#define BOUT_NVECTOR_H + +#include +#include +#include +#include +#include +#include +#include +#if BOUT_HAS_SUNDIALS_MANYVECTOR +#include +#endif +#include +#include + +#include "bout/sundials_backports.hxx" + +class BoutNVector { +private: + static double all_reduce(const double local, const MPI_Op op = MPI_SUM) { + double global; + MPI_Allreduce(&local, &global, 1, MPI_DOUBLE, op, BoutComm::get()); + return global; + } + + static double all_reduce(const unsigned int local, const MPI_Op op = MPI_SUM) { + unsigned int global; + MPI_Allreduce(&local, &global, 1, MPI_UNSIGNED, op, BoutComm::get()); + return global; + } + + template + struct Content { + T& field; + const bool own; + const bool evolve_bndry; + const sunindextype length; + + Content(T& field, const bool own, const bool evolve_bndry) + : field(field), own(own), evolve_bndry(evolve_bndry), + length(all_reduce(getRegion().size())) {} + + auto getRegion() const { + return field.getRegion(evolve_bndry ? RGN_ALL : RGN_NOBNDRY); + } + + ~Content() { + if (own) { + delete &field; + } + } + }; + + template + static Content& get_content(const N_Vector v) { + return *static_cast*>(v->content); + } + + template + static T& get_field(const N_Vector v) { + return get_content(v).field; + } + + template + static BoutReal reduce_field(Reduce&& reduce, const bool allpe, const N_Vector v1, + const V... v2) { + BoutReal result = 0; + const auto region = get_content(v1).getRegion(); + BOUT_FOR_OMP(i, region, parallel for reduction(+:result)) { + result += std::invoke(std::forward(reduce), get_field(v1)[i], + get_field(v2)[i]...); + } + + return allpe ? all_reduce(result) : result; + } + +public: + BoutNVector() = delete; // Enforce static access only + + template > + static N_Vector create(Ctx&& ctx, T& field, const bool evolve_bndry, + const bool own = false) { + N_Vector v = callWithSUNContext(N_VNewEmpty, std::forward(ctx)); + if (v == nullptr) { + throw BoutException("N_VNewEmpty failed\n"); + } + + v->content = static_cast(new Content(field, own, evolve_bndry)); + + v->ops->nvgetvectorid = [](N_Vector) { return SUNDIALS_NVEC_CUSTOM; }; + + v->ops->nvclone = [](N_Vector x) { + const Content& content = get_content(x); + // TODO: ensure no memory leaks + // T* field_clone = new T(*content.field); + T* field_clone = new T(content.field.getMesh(), content.field.getLocation(), + content.field.getDirections()); + field_clone->allocate(); + field_clone->copyBoundary(content.field); + return create(x->sunctx, *field_clone, content.evolve_bndry, true); + }; + + v->ops->nvdestroy = [](N_Vector x) { + if (x == nullptr) { + return; + } + delete &get_content(x); + N_VFreeEmpty(x); + }; + + v->ops->nvgetlength = [](N_Vector x) { return get_content(x).length; }; + + v->ops->nvlinearsum = [](sunrealtype a, N_Vector x, sunrealtype b, N_Vector y, + N_Vector z) { + get_field(z) = a * get_field(x) + b * get_field(y); + }; + + v->ops->nvconst = [](sunrealtype c, N_Vector x) { get_field(x) = c; }; + + v->ops->nvprod = [](N_Vector x, N_Vector y, N_Vector z) { + get_field(z) = get_field(x) * get_field(y); + }; + + v->ops->nvdiv = [](N_Vector x, N_Vector y, N_Vector z) { + get_field(z) = get_field(x) / get_field(y); + }; + + v->ops->nvscale = [](sunrealtype a, N_Vector x, N_Vector y) { + get_field(y) = a * get_field(x); + }; + + v->ops->nvabs = [](N_Vector x, N_Vector y) { + get_field(y) = abs(get_field(x)); + }; + + v->ops->nvinv = [](N_Vector x, N_Vector y) { get_field(y) = 1 / get_field(x); }; + + v->ops->nvaddconst = [](N_Vector x, sunrealtype a, N_Vector y) { + get_field(y) = a + get_field(x); + }; + + v->ops->nvdotprod = [](N_Vector x, N_Vector y) { + return reduce_field(std::multiplies(), true, x, y); + }; + + v->ops->nvmaxnorm = [](N_Vector x) { return max(abs(get_field(x)), true); }; + + v->ops->nvwrmsnorm = [](N_Vector x, N_Vector y) { + return N_VWL2Norm(x, y) / std::sqrt(static_cast(N_VGetLength(x))); + }; + + v->ops->nvmin = [](N_Vector x) { return min(get_field(x), true); }; + + v->ops->nvwl2norm = [](N_Vector x, N_Vector y) { + return std::sqrt(reduce_field( + [](const auto v1, const auto v2) { return std::pow(v1 * v2, 2); }, true, x, y)); + }; + + v->ops->nvl1norm = [](N_Vector x) { + return reduce_field([](const auto value) { return std::abs(value); }, true, x); + }; + + return v; + } + + template > + static void swap(const N_Vector v, T& field) { + field.swapData(get_field(v)); + } + + template > + static T& get(const N_Vector v) { + return get_field(v); + } + +#if BOUT_HAS_SUNDIALS_MANYVECTOR + template + static N_Vector create(const sundials::Context& ctx, V& subvectors) { + const auto v = callWithSUNContext(N_VNew_ManyVector, ctx, std::size(subvectors), + std::data(subvectors)); + ((N_VectorContent_ManyVector)v->content)->own_data = true; + return v; + } + + template > + static void swap(const N_Vector v, T& field, std::size_t subvector) { + return BoutNVector::swap(N_VGetSubvector_ManyVector(v, subvector), field); + } + + template > + static T& get(const N_Vector v, std::size_t subvector) { + return BoutNVector::get(N_VGetSubvector_ManyVector(v, subvector)); + } +#endif +}; + +#endif + +/* Other functions to implement (most optional) +N_Vector (*nvcloneempty)(N_Vector); // Probably not needed +void (*nvdestroy)(N_Vector); // Implemented +void (*nvspace)(N_Vector, sunindextype*, sunindextype*); // Not needed +sunrealtype* (*nvgetarraypointer)(N_Vector); // Probably not needed +sunrealtype* (*nvgetdevicearraypointer)(N_Vector); // Probably not needed +void (*nvsetarraypointer)(sunrealtype*, N_Vector); // Probably not needed +SUNComm (*nvgetcommunicator)(N_Vector); // Probably not needed +sunindextype (*nvgetlength)(N_Vector); // Implemented +sunindextype (*nvgetlocallength)(N_Vector); // Probably not needed +void (*nvlinearsum)(sunrealtype, N_Vector, sunrealtype, N_Vector, N_Vector); // Implemented +void (*nvconst)(sunrealtype, N_Vector); // Implemented +void (*nvprod)(N_Vector, N_Vector, N_Vector); // Implemented +void (*nvdiv)(N_Vector, N_Vector, N_Vector); // Implemented +void (*nvscale)(sunrealtype, N_Vector, N_Vector); // Implemented +void (*nvabs)(N_Vector, N_Vector); // Implemented +void (*nvinv)(N_Vector, N_Vector); // Implemented +void (*nvaddconst)(N_Vector, sunrealtype, N_Vector); // Implemented +sunrealtype (*nvdotprod)(N_Vector, N_Vector); // Implemented +sunrealtype (*nvmaxnorm)(N_Vector); // Implemented +sunrealtype (*nvwrmsnorm)(N_Vector, N_Vector); // Implemented +sunrealtype (*nvwrmsnormmask)(N_Vector, N_Vector, N_Vector); // TODO +sunrealtype (*nvmin)(N_Vector); // Implemented +sunrealtype (*nvwl2norm)(N_Vector, N_Vector); // Implemented +sunrealtype (*nvl1norm)(N_Vector); // Implemented +void (*nvcompare)(sunrealtype, N_Vector, N_Vector); // TODO +sunbooleantype (*nvinvtest)(N_Vector, N_Vector); // TODO +sunbooleantype (*nvconstrmask)(N_Vector, N_Vector, N_Vector); // TODO +sunrealtype (*nvminquotient)(N_Vector, N_Vector); // TODO +SUNErrCode (*nvlinearcombination)(int, sunrealtype*, N_Vector*, N_Vector); // TODO +SUNErrCode (*nvscaleaddmulti)(int, sunrealtype*, N_Vector, N_Vector*, + N_Vector*); // Probably not needed +SUNErrCode (*nvdotprodmulti)(int, N_Vector, N_Vector*, sunrealtype*); // Probably not needed +SUNErrCode (*nvlinearsumvectorarray)(int, sunrealtype, N_Vector*, sunrealtype, + N_Vector*, N_Vector*); // Probably not needed +SUNErrCode (*nvscalevectorarray)(int, sunrealtype*, N_Vector*, N_Vector*); // Probably not needed +SUNErrCode (*nvconstvectorarray)(int, sunrealtype, N_Vector*); // Probably not needed +SUNErrCode (*nvwrmsnormvectorarray)(int, N_Vector*, N_Vector*, sunrealtype*); // Probably not needed +SUNErrCode (*nvwrmsnormmaskvectorarray)(int, N_Vector*, N_Vector*, N_Vector, + sunrealtype*); // Probably not needed +SUNErrCode (*nvscaleaddmultivectorarray)(int, int, sunrealtype*, N_Vector*, + N_Vector**, N_Vector**); // Probably not needed +SUNErrCode (*nvlinearcombinationvectorarray)(int, int, sunrealtype*, + N_Vector**, N_Vector*); // Probably not needed +sunrealtype (*nvdotprodlocal)(N_Vector, N_Vector); // Probably not needed +sunrealtype (*nvmaxnormlocal)(N_Vector); // Probably not needed +sunrealtype (*nvminlocal)(N_Vector); // Probably not needed +sunrealtype (*nvl1normlocal)(N_Vector); // Probably not needed +sunbooleantype (*nvinvtestlocal)(N_Vector, N_Vector); // Probably not needed +sunbooleantype (*nvconstrmasklocal)(N_Vector, N_Vector, N_Vector); // Probably not needed +sunrealtype (*nvminquotientlocal)(N_Vector, N_Vector); // Probably not needed +sunrealtype (*nvwsqrsumlocal)(N_Vector, N_Vector); // Probably not needed +sunrealtype (*nvwsqrsummasklocal)(N_Vector, N_Vector, N_Vector); // Probably not needed +SUNErrCode (*nvdotprodmultilocal)(int, N_Vector, N_Vector*, sunrealtype*); // Probably not needed +SUNErrCode (*nvdotprodmultiall_reduce)(int, N_Vector, sunrealtype*); // Probably not needed +SUNErrCode (*nvbufsize)(N_Vector, sunindextype*); // Probably not needed +SUNErrCode (*nvbufpack)(N_Vector, void*); // Probably not needed +SUNErrCode (*nvbufunpack)(N_Vector, void*); // Probably not needed +void (*nvprint)(N_Vector); // Nice, but optional +void (*nvprintfile)(N_Vector, FILE*); // Probably not needed +*/ diff --git a/src/solver/petsc_preconditioner.cxx b/src/solver/petsc_preconditioner.cxx new file mode 100644 index 0000000000..508d001fd3 --- /dev/null +++ b/src/solver/petsc_preconditioner.cxx @@ -0,0 +1,326 @@ +#include "bout/build_defines.hxx" + +#if BOUT_HAS_PETSC + +#include "bout/petsc_preconditioner.hxx" + +#include "bout/assert.hxx" +#include "bout/boutcomm.hxx" +#include "bout/field3d.hxx" +#include "bout/globals.hxx" +#include "bout/mesh.hxx" +#include "bout/options.hxx" +#include "bout/output.hxx" +#include "bout/utils.hxx" + +#include +#include +#include +#include +#include + +namespace { +class ColoringStencil { +private: + static bool isInSquare(int i, int j, int n_square) { + return std::abs(i) <= n_square && std::abs(j) <= n_square; + } + static bool isInCross(int i, int j, int n_cross) { + if (i == 0) { + return std::abs(j) <= n_cross; + } + if (j == 0) { + return std::abs(i) <= n_cross; + } + return false; + } + static bool isInTaxi(int i, int j, int n_taxi) { + return std::abs(i) + std::abs(j) <= n_taxi; + } + +public: + static auto getOffsets(int n_square, int n_taxi, int n_cross) { + ASSERT2(n_square >= 0 && n_cross >= 0 && n_taxi >= 0 + && n_square + n_cross + n_taxi > 0); + auto inside = [&](int i, int j) { + return isInSquare(i, j, n_square) || isInTaxi(i, j, n_taxi) + || isInCross(i, j, n_cross); + }; + std::vector> xy_offsets; + auto loop_bound = std::max({n_square, n_taxi, n_cross}); + for (int i = -loop_bound; i <= loop_bound; ++i) { + for (int j = -loop_bound; j <= loop_bound; ++j) { + if (inside(i, j)) { + xy_offsets.emplace_back(i, j); + } + } + } + return xy_offsets; + } +}; +} // namespace + +void PetscPreconditioner::reset() { + if (fdcoloring != nullptr) { + MatFDColoringDestroy(&fdcoloring); + fdcoloring = nullptr; + } + if (Jfd != nullptr) { + MatDestroy(&Jfd); + Jfd = nullptr; + } +} + +PetscErrorCode PetscPreconditioner::createJacobianPattern(Field3D& index, + Options& options, + PetscInt nlocal, int n2d, + int n3d, MPI_Comm comm) { + reset(); + + // Use global mesh for now + Mesh* mesh = bout::globals::mesh; + + ////////////////////////////////////////////////// + // Pre-allocate PETSc storage + + output_progress.write("Setting Jacobian matrix sizes\n"); + + // Set size of Matrix on each processor to nlocal x nlocal + PetscCall(MatCreate(comm, &Jfd)); + PetscCall(MatSetOption(Jfd, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE)); + PetscCall(MatSetSizes(Jfd, nlocal, nlocal, PETSC_DETERMINE, PETSC_DETERMINE)); + PetscCall(MatSetFromOptions(Jfd)); + + // Determine which row/columns of the matrix are locally owned + int Istart = 0; + int Iend = 0; + PetscCall(MatGetOwnershipRange(Jfd, &Istart, &Iend)); + + // Convert local into global indices + // Note: Not in the boundary cells, to keep -1 values + for (const auto& i : mesh->getRegion3D("RGN_NOBNDRY")) { + index[i] += Istart; + } + // Now communicate to fill guard cells + mesh->communicate(index); + + // Non-zero elements on this processor + std::vector d_nnz; + std::vector o_nnz; + + auto n_square = + options["stencil:square"].doc("Extent of stencil (square)").withDefault(0); + auto n_cross = + options["stencil:cross"].doc("Extent of stencil (cross)").withDefault(0); + // Default n_taxi=2 if nothing else is set + auto n_taxi = options["stencil:taxi"] + .doc("Extent of stencil (taxi-cab norm)") + .withDefault((n_square == 0 && n_cross == 0) ? 2 : 0); + + const auto xy_offsets = ColoringStencil::getOffsets(n_square, n_taxi, n_cross); + { + // Count the *unique* non-zeros for preallocation + std::vector> d_nnz_map2d(nlocal); + std::vector> o_nnz_map2d(nlocal); + std::vector> d_nnz_map3d(nlocal); + std::vector> o_nnz_map3d(nlocal); + + for (int x = mesh->xstart; x <= mesh->xend; x++) { + for (int y = mesh->ystart; y <= mesh->yend; y++) { + const int ind0 = ROUND(index(x, y, 0)) - Istart; + + // 2D fields + for (int i = 0; i < n2d; i++) { + const PetscInt row = ind0 + i; + for (const auto& [x_off, y_off] : xy_offsets) { + const int xi = x + x_off; + const int yi = y + y_off; + if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) || (yi >= mesh->LocalNy)) { + continue; + } + + const int ind2 = ROUND(index(xi, yi, 0)); + if (ind2 < 0) { + continue; // A boundary point + } + + for (int j = 0; j < n2d; j++) { + const PetscInt col = ind2 + j; + if (col >= Istart && col < Iend) { + d_nnz_map2d[row].insert(col); + } else { + o_nnz_map2d[row].insert(col); + } + } + } + } + + // 3D fields + for (int z = mesh->zstart; z <= mesh->zend; z++) { + const int ind = ROUND(index(x, y, z)) - Istart; + + for (int i = 0; i < n3d; i++) { + PetscInt row = ind + i; + if (z == 0) { + row += n2d; + } + + // Depends on 2D fields + for (int j = 0; j < n2d; j++) { + const PetscInt col = ind0 + j; + if (col >= Istart && col < Iend) { + d_nnz_map2d[row].insert(col); + } else { + o_nnz_map2d[row].insert(col); + } + } + + for (const auto& [x_off, y_off] : xy_offsets) { + const int xi = x + x_off; + const int yi = y + y_off; + + if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) + || (yi >= mesh->LocalNy)) { + continue; + } + + int ind2 = ROUND(index(xi, yi, 0)); + if (ind2 < 0) { + continue; // Boundary point + } + + if (z == 0) { + ind2 += n2d; + } + + for (int j = 0; j < n3d; j++) { + const PetscInt col = ind2 + j; + if (col >= Istart && col < Iend) { + d_nnz_map3d[row].insert(col); + } else { + o_nnz_map3d[row].insert(col); + } + } + } + } + } + } + } + + d_nnz.reserve(nlocal); + o_nnz.reserve(nlocal); + for (int i = 0; i < nlocal; ++i) { + // Assume all elements in the z direction are potentially coupled + d_nnz.emplace_back((d_nnz_map3d[i].size() * mesh->LocalNz) + d_nnz_map2d[i].size()); + o_nnz.emplace_back((o_nnz_map3d[i].size() * mesh->LocalNz) + o_nnz_map2d[i].size()); + } + } + + output_progress.write("Pre-allocating Jacobian\n"); + PetscCall(MatMPIAIJSetPreallocation(Jfd, 0, d_nnz.data(), 0, o_nnz.data())); + PetscCall(MatSeqAIJSetPreallocation(Jfd, 0, d_nnz.data())); + PetscCall(MatSetUp(Jfd)); + PetscCall(MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE)); + + ////////////////////////////////////////////////// + // Mark non-zero entries + + output_progress.write("Marking non-zero Jacobian entries\n"); + const PetscScalar val = 1.0; + for (int x = mesh->xstart; x <= mesh->xend; x++) { + for (int y = mesh->ystart; y <= mesh->yend; y++) { + const int ind0 = ROUND(index(x, y, 0)); + + // 2D fields + for (int i = 0; i < n2d; i++) { + const PetscInt row = ind0 + i; + + for (const auto& [x_off, y_off] : xy_offsets) { + const int xi = x + x_off; + const int yi = y + y_off; + if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) || (yi >= mesh->LocalNy)) { + continue; + } + + const int ind2 = ROUND(index(xi, yi, 0)); + if (ind2 < 0) { + continue; // A boundary point + } + + for (int j = 0; j < n2d; j++) { + const PetscInt col = ind2 + j; + PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); + } + } + } + + // 3D fields + for (int z = mesh->zstart; z <= mesh->zend; z++) { + const int ind = ROUND(index(x, y, z)); + + for (int i = 0; i < n3d; i++) { + PetscInt row = ind + i; + if (z == 0) { + row += n2d; + } + + // Depends on 2D fields + for (int j = 0; j < n2d; j++) { + const PetscInt col = ind0 + j; + PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); + } + + for (const auto& [x_off, y_off] : xy_offsets) { + const int xi = x + x_off; + const int yi = y + y_off; + + if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) || (yi >= mesh->LocalNy)) { + continue; + } + + for (int zi = mesh->zstart; zi <= mesh->zend; ++zi) { + int ind2 = ROUND(index(xi, yi, zi)); + if (ind2 < 0) { + continue; // Boundary point + } + + if (z == 0) { + ind2 += n2d; + } + + for (int j = 0; j < n3d; j++) { + const PetscInt col = ind2 + j; + PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); + } + } + } + } + } + } + } + + output_progress.write("Assembling Jacobian matrix\n"); + PetscCall(MatAssemblyBegin(Jfd, MAT_FINAL_ASSEMBLY)); + PetscCall(MatAssemblyEnd(Jfd, MAT_FINAL_ASSEMBLY)); + + { + PetscBool symmetric = PETSC_FALSE; + PetscCall(MatIsSymmetric(Jfd, 1e-5, &symmetric)); + if (!static_cast(symmetric)) { + output_warn.write("Jacobian pattern is not symmetric\n"); + } + } + + if (options["force_symmetric_coloring"] + .doc("Modifies coloring matrix to force it to be symmetric") + .withDefault(false)) { + Mat Jfd_T{nullptr}; + PetscCall(MatCreateTranspose(Jfd, &Jfd_T)); + PetscCall(MatAXPY(Jfd, 1, Jfd_T, DIFFERENT_NONZERO_PATTERN)); + PetscCall(MatDestroy(&Jfd_T)); + } + + PetscFunctionReturn(PETSC_SUCCESS); +} + +#endif // BOUT_HAS_PETSC diff --git a/src/solver/solver.cxx b/src/solver/solver.cxx index a3f6a874f6..1f7b654427 100644 --- a/src/solver/solver.cxx +++ b/src/solver/solver.cxx @@ -1,8 +1,8 @@ /************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov * - * Contact: Ben Dudson, bd512@york.ac.uk - * * This file is part of BOUT++. * * BOUT++ is free software: you can redistribute it and/or modify @@ -24,23 +24,38 @@ #include "bout/array.hxx" #include "bout/assert.hxx" +#include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" #include "bout/field_factory.hxx" +#include "bout/globals.hxx" #include "bout/initialprofiles.hxx" #include "bout/interpolation.hxx" +#include "bout/monitor.hxx" #include "bout/msg_stack.hxx" +#include "bout/options.hxx" #include "bout/output.hxx" #include "bout/region.hxx" #include "bout/solver.hxx" +#include "bout/sys/gettext.hxx" #include "bout/sys/timer.hxx" #include "bout/sys/uuid.h" +#include "bout/unused.hxx" +#include "bout/utils.hxx" +#include "bout/vector2d.hxx" +#include "bout/vector3d.hxx" + +#include #include -#include #include +#include #include #include +#include +#include // Implementations: #include "impls/adams_bashforth/adams_bashforth.hxx" @@ -507,11 +522,11 @@ int Solver::solve(int nout, BoutReal timestep) { finaliseMonitorPeriods(nout, timestep); output_progress.write( - _("Solver running for {:d} outputs with output timestep of {:e}\n"), nout, + _f("Solver running for {:d} outputs with output timestep of {:e}\n"), nout, timestep); if (default_monitor_period > 1) { output_progress.write( - _("Solver running for {:d} outputs with monitor timestep of {:e}\n"), + _f("Solver running for {:d} outputs with monitor timestep of {:e}\n"), nout / default_monitor_period, timestep * default_monitor_period); } @@ -537,7 +552,7 @@ int Solver::solve(int nout, BoutReal timestep) { } time_t start_time = time(nullptr); - output_progress.write(_("\nRun started at : {:s}\n"), toString(start_time)); + output_progress.write(_f("\nRun started at : {:s}\n"), toString(start_time)); Timer timer("run"); // Start timer @@ -583,7 +598,7 @@ int Solver::solve(int nout, BoutReal timestep) { status = run(); time_t end_time = time(nullptr); - output_progress.write(_("\nRun finished at : {:s}\n"), toString(end_time)); + output_progress.write(_f("\nRun finished at : {:s}\n"), toString(end_time)); output_progress.write(_("Run time : ")); int dt = end_time - start_time; @@ -636,7 +651,7 @@ std::string Solver::createRunID() const { } std::string Solver::getRunID() const { - AUTO_TRACE(); + if (run_id == default_run_id) { throw BoutException("run_id not set!"); } @@ -644,7 +659,7 @@ std::string Solver::getRunID() const { } std::string Solver::getRunRestartFrom() const { - AUTO_TRACE(); + // Check against run_id, because this might not be a restarted run if (run_id == default_run_id) { throw BoutException("run_restart_from not set!"); @@ -664,9 +679,6 @@ void Solver::writeToModelOutputFile(const Options& options) { **************************************************************************/ int Solver::init() { - - TRACE("Solver::init()"); - if (initialised) { throw BoutException(_("ERROR: Solver is already initialised\n")); } @@ -730,7 +742,7 @@ void Solver::outputVars(Options& output_options, bool save_repeat) { void Solver::readEvolvingVariablesFromOptions(Options& options) { run_id = options["run_id"].withDefault(default_run_id); - simtime = options["tt"].as(); + simtime = options["tt"].withDefault(0.0); iteration = options["hist_hi"].withDefault(0); iteration_offset = iteration; @@ -769,7 +781,7 @@ BoutReal Solver::adjustMonitorPeriods(Monitor* new_monitor) { } if (!isMultiple(internal_timestep, new_monitor->timestep)) { - throw BoutException(_("Couldn't add Monitor: {:g} is not a multiple of {:g}!"), + throw BoutException(_f("Couldn't add Monitor: {:g} is not a multiple of {:g}!"), internal_timestep, new_monitor->timestep); } @@ -785,8 +797,8 @@ BoutReal Solver::adjustMonitorPeriods(Monitor* new_monitor) { if (initialised) { throw BoutException( - _("Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) " - "after init is called!"), + _f("Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) " + "after init is called!"), internal_timestep, new_monitor->timestep); } @@ -886,7 +898,7 @@ int Solver::call_monitors(BoutReal simtime, int iter, int NOUT) { monitor.monitor->call(this, simtime, iter / monitor.monitor->period, NOUT / monitor.monitor->period); if (ret != 0) { - throw BoutException(_("Monitor signalled to quit (return code {})"), ret); + throw BoutException(_f("Monitor signalled to quit (return code {})"), ret); } // Write the monitor's diagnostics to the main output file Options monitor_dump; @@ -908,7 +920,7 @@ int Solver::call_monitors(BoutReal simtime, int iter, int NOUT) { for (const auto& monitor : monitors) { monitor.monitor->cleanup(); } - output_error.write(_("Monitor signalled to quit (exception {})\n"), e.what()); + output_error.write(_f("Monitor signalled to quit (exception {})\n"), e.what()); throw; } @@ -1016,179 +1028,6 @@ std::unique_ptr Solver::create(const SolverType& type, Options* opts) { return SolverFactory::getInstance().create(type, opts); } -/************************************************************************** - * Looping over variables - * - * NOTE: This part is very inefficient, and should be replaced ASAP - * Is the interleaving of variables needed or helpful to the solver? - **************************************************************************/ - -/// Perform an operation at a given Ind2D (jx,jy) location, moving data between BOUT++ and CVODE -void Solver::loop_vars_op(Ind2D i2d, BoutReal* udata, int& p, SOLVER_VAR_OP op, - bool bndry) { - // Use global mesh: FIX THIS! - Mesh* mesh = bout::globals::mesh; - - int nz = mesh->LocalNz; - - switch (op) { - case SOLVER_VAR_OP::LOAD_VARS: { - /// Load variables from IDA into BOUT++ - - // Loop over 2D variables - for (const auto& f : f2d) { - if (bndry && !f.evolve_bndry) { - continue; - } - (*f.var)[i2d] = udata[p]; - p++; - } - - for (int jz = 0; jz < nz; jz++) { - - // Loop over 3D variables - for (const auto& f : f3d) { - if (bndry && !f.evolve_bndry) { - continue; - } - (*f.var)[f.var->getMesh()->ind2Dto3D(i2d, jz)] = udata[p]; - p++; - } - } - break; - } - case SOLVER_VAR_OP::LOAD_DERIVS: { - /// Load derivatives from IDA into BOUT++ - /// Used for preconditioner - - // Loop over 2D variables - for (const auto& f : f2d) { - if (bndry && !f.evolve_bndry) { - continue; - } - (*f.F_var)[i2d] = udata[p]; - p++; - } - - for (int jz = 0; jz < nz; jz++) { - - // Loop over 3D variables - for (const auto& f : f3d) { - if (bndry && !f.evolve_bndry) { - continue; - } - (*f.F_var)[f.F_var->getMesh()->ind2Dto3D(i2d, jz)] = udata[p]; - p++; - } - } - - break; - } - case SOLVER_VAR_OP::SET_ID: { - /// Set the type of equation (Differential or Algebraic) - - // Loop over 2D variables - for (const auto& f : f2d) { - if (bndry && !f.evolve_bndry) { - continue; - } - if (f.constraint) { - udata[p] = 0; - } else { - udata[p] = 1; - } - p++; - } - - for (int jz = 0; jz < nz; jz++) { - - // Loop over 3D variables - for (const auto& f : f3d) { - if (bndry && !f.evolve_bndry) { - continue; - } - if (f.constraint) { - udata[p] = 0; - } else { - udata[p] = 1; - } - p++; - } - } - - break; - } - case SOLVER_VAR_OP::SAVE_VARS: { - /// Save variables from BOUT++ into IDA (only used at start of simulation) - - // Loop over 2D variables - for (const auto& f : f2d) { - if (bndry && !f.evolve_bndry) { - continue; - } - udata[p] = (*f.var)[i2d]; - p++; - } - - for (int jz = 0; jz < nz; jz++) { - - // Loop over 3D variables - for (const auto& f : f3d) { - if (bndry && !f.evolve_bndry) { - continue; - } - udata[p] = (*f.var)[f.var->getMesh()->ind2Dto3D(i2d, jz)]; - p++; - } - } - break; - } - /// Save time-derivatives from BOUT++ into CVODE (returning RHS result) - case SOLVER_VAR_OP::SAVE_DERIVS: { - - // Loop over 2D variables - for (const auto& f : f2d) { - if (bndry && !f.evolve_bndry) { - continue; - } - udata[p] = (*f.F_var)[i2d]; - p++; - } - - for (int jz = 0; jz < nz; jz++) { - - // Loop over 3D variables - for (const auto& f : f3d) { - if (bndry && !f.evolve_bndry) { - continue; - } - udata[p] = (*f.F_var)[f.F_var->getMesh()->ind2Dto3D(i2d, jz)]; - p++; - } - } - break; - } - } -} - -/// Loop over variables and domain. Used for all data operations for consistency -void Solver::loop_vars(BoutReal* udata, SOLVER_VAR_OP op) { - // Use global mesh: FIX THIS! - Mesh* mesh = bout::globals::mesh; - - int p = 0; // Counter for location in udata array - - // All boundaries - for (const auto& i2d : mesh->getRegion2D("RGN_BNDRY")) { - loop_vars_op(i2d, udata, p, op, true); - } - - // Bulk of points - for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { - loop_vars_op(i2d, udata, p, op, false); - } -} - void Solver::load_vars(BoutReal* udata) { // Make sure data is allocated for (const auto& f : f2d) { @@ -1199,7 +1038,8 @@ void Solver::load_vars(BoutReal* udata) { f.var->setLocation(f.location); } - loop_vars(udata, SOLVER_VAR_OP::LOAD_VARS); + loop_vars(VarRange(f2d), + VarRange(f3d), udata, SOLVER_VAR_OP::LOAD); // Mark each vector as either co- or contra-variant @@ -1221,7 +1061,8 @@ void Solver::load_derivs(BoutReal* udata) { f.F_var->setLocation(f.location); } - loop_vars(udata, SOLVER_VAR_OP::LOAD_DERIVS); + loop_vars(VarRange(f2d), + VarRange(f3d), udata, SOLVER_VAR_OP::LOAD); // Mark each vector as either co- or contra-variant @@ -1237,13 +1078,13 @@ void Solver::load_derivs(BoutReal* udata) { void Solver::save_vars(BoutReal* udata) { for (const auto& f : f2d) { if (!f.var->isAllocated()) { - throw BoutException(_("Variable '{:s}' not initialised"), f.name); + throw BoutException(_f("Variable '{:s}' not initialised"), f.name); } } for (const auto& f : f3d) { if (!f.var->isAllocated()) { - throw BoutException(_("Variable '{:s}' not initialised"), f.name); + throw BoutException(_f("Variable '{:s}' not initialised"), f.name); } } @@ -1263,7 +1104,8 @@ void Solver::save_vars(BoutReal* udata) { } } - loop_vars(udata, SOLVER_VAR_OP::SAVE_VARS); + loop_vars(VarRange(f2d), + VarRange(f3d), udata, SOLVER_VAR_OP::SAVE); } void Solver::save_derivs(BoutReal* dudata) { @@ -1286,17 +1128,21 @@ void Solver::save_derivs(BoutReal* dudata) { // Make sure 3D fields are at the correct cell location for (const auto& f : f3d) { if (f.var->getLocation() != (f.F_var)->getLocation()) { - throw BoutException(_("Time derivative at wrong location - Field is at {:s}, " - "derivative is at {:s} for field '{:s}'\n"), + throw BoutException(_f("Time derivative at wrong location - Field is at {:s}, " + "derivative is at {:s} for field '{:s}'\n"), toString(f.var->getLocation()), toString(f.F_var->getLocation()), f.name); } } - loop_vars(dudata, SOLVER_VAR_OP::SAVE_DERIVS); + loop_vars(VarRange(f2d), + VarRange(f3d), dudata, SOLVER_VAR_OP::SAVE); } -void Solver::set_id(BoutReal* udata) { loop_vars(udata, SOLVER_VAR_OP::SET_ID); } +void Solver::set_id(BoutReal* udata) { + loop_vars(VarRange(f2d), + VarRange(f3d), udata, SOLVER_VAR_OP::SET_ID); +} Field3D Solver::globalIndex(int localStart) { // Use global mesh: FIX THIS! @@ -1491,7 +1337,7 @@ void Solver::post_rhs(BoutReal UNUSED(t)) { #if CHECK > 0 for (const auto& f : f3d) { if (!f.F_var->isAllocated()) { - throw BoutException(_("Time derivative for variable '{:s}' not set"), f.name); + throw BoutException(_f("Time derivative for variable '{:s}' not set"), f.name); } } #endif diff --git a/src/solver/sundials_nvector_interface.hxx b/src/solver/sundials_nvector_interface.hxx new file mode 100644 index 0000000000..d88221f259 --- /dev/null +++ b/src/solver/sundials_nvector_interface.hxx @@ -0,0 +1,341 @@ +/************************************************************************** + * Shared helpers for SUNDIALS N_Vector backends used by BOUT++ solvers. + * + * Copyright 2010-2026 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov + * + * This file is part of BOUT++. + * + * BOUT++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * BOUT++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with BOUT++. If not, see . + * + **************************************************************************/ + +#ifndef BOUT_SUNDIALS_NVECTOR_INTERFACE_HXX +#define BOUT_SUNDIALS_NVECTOR_INTERFACE_HXX + +#include "bout/bout_enum_class.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutcomm.hxx" +#include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" +#include "bout/field2d.hxx" +#include "bout/globals.hxx" +#include "bout/region.hxx" +#include "bout/solver.hxx" +#include "bout/sundials_backports.hxx" +#include "bout/unused.hxx" + +#if BOUT_HAS_SUNDIALS_MANYVECTOR +#include "nvector.hxx" +#endif + +#include +#include +#include + +/// Supported SUNDIALS ``N_Vector`` backends that can be selected at runtime. +BOUT_ENUM_CLASS(NVectorType, Sundials, ManyVector); + +/// Helper for moving solver state between BOUT++ fields and a chosen ``N_Vector`` +/// backend without exposing backend-specific details in each solver wrapper. +class SundialsNVectorInterface { +public: + SundialsNVectorInterface(Solver& solver_in, const sundials::Context& ctx_in, + NVectorType nvector_type_in) + : solver(solver_in), ctx(ctx_in), nvector_type(nvector_type_in) {} + + /// Return ``true`` when using the field-backed ManyVector implementation. + bool use_manyvector() const { return nvector_type == NVectorType::ManyVector; } + + /// Human-readable backend name for logs and error messages. + const char* backend_name() const { + return use_manyvector() ? "SUNDIALS ManyVector-backed custom" : "SUNDIALS parallel"; + } + + /// Throw if the selected backend requires SUNDIALS ManyVector support that was + /// not enabled when BOUT++ was built. + void ensure_manyvector_available() const { + if (use_manyvector() and not BOUT_HAS_SUNDIALS_MANYVECTOR) { + throw BoutException("SUNDIALS ManyVector backend requested, but BOUT++ was " + "built without SUNDIALS ManyVector support"); + } + } + + /// Create the main solver state vector using the selected backend. + N_Vector create_state_vector(int local_N, int neq) const { + ensure_manyvector_available(); + + if (use_manyvector()) { +#if BOUT_HAS_SUNDIALS_MANYVECTOR + auto* vec = nvector_from_state(); + if (vec == nullptr) { + throw BoutException("BOUT N_Vector failed\n"); + } + return vec; +#endif + } + + auto* vec = callWithSUNContext(N_VNew_Parallel, ctx, BoutComm::get(), local_N, neq); + if (vec == nullptr) { + throw BoutException("SUNDIALS memory allocation failed\n"); + } + solver.save_vars(N_VGetArrayPointer(vec)); + return vec; + } + + /// Clone a vector of the same backend/layout, with a descriptive error on failure. + N_Vector clone_vector_like(N_Vector source, const char* description) const { + auto* vec = N_VClone(source); + if (vec == nullptr) { + throw BoutException("{}\n", description); + } + return vec; + } + + /// Copy the current solution state from ``u`` into the solver fields. + void copy_state_from_vector(N_Vector u) const { + ensure_manyvector_available(); + + if (use_manyvector()) { +#if BOUT_HAS_SUNDIALS_MANYVECTOR + swap_state(u); + return; +#endif + } + + solver.load_vars(N_VGetArrayPointer(u)); + } + + /// Copy the solver fields into the solution vector ``u``. + void copy_state_to_vector(N_Vector u) const { + ensure_manyvector_available(); + + if (use_manyvector()) { +#if BOUT_HAS_SUNDIALS_MANYVECTOR + swap_state(u); + return; +#endif + } + + solver.save_vars(N_VGetArrayPointer(u)); + } + + /// Copy time-derivative values from ``du`` into the solver derivative fields. + void copy_deriv_from_vector(N_Vector du) const { + ensure_manyvector_available(); + + if (use_manyvector()) { +#if BOUT_HAS_SUNDIALS_MANYVECTOR + swap_deriv(du); + return; +#endif + } + + solver.load_derivs(N_VGetArrayPointer(du)); + } + + /// Copy the solver derivative fields into ``du``. + void copy_deriv_to_vector(N_Vector du) const { + ensure_manyvector_available(); + + if (use_manyvector()) { +#if BOUT_HAS_SUNDIALS_MANYVECTOR + swap_deriv(du); + return; +#endif + } + + solver.save_derivs(N_VGetArrayPointer(du)); + } + + /// Fill every evolved entry in ``vec`` with per-field constants, respecting + /// whether each field evolves boundary cells. + void fill_vector_values(N_Vector vec, const std::vector& f2d_values, + const std::vector& f3d_values) const { + ensure_manyvector_available(); + + if (use_manyvector()) { +#if BOUT_HAS_SUNDIALS_MANYVECTOR + std::size_t i = 0; + for (std::size_t j = 0; j < f2d_values.size(); ++j, ++i) { + fill_field(BoutNVector::get(vec, i), f2d_values[j], + solver.f2d[j].evolve_bndry); + } + for (std::size_t j = 0; j < f3d_values.size(); ++j, ++i) { + fill_field(BoutNVector::get(vec, i), f3d_values[j], + solver.f3d[j].evolve_bndry); + } + return; +#endif + } + + BoutReal* option_data = N_VGetArrayPointer(vec); + int p = 0; + for (const auto& i2d : bout::globals::mesh->getRegion2D("RGN_BNDRY")) { + fill_vector_values_op(i2d, option_data, p, f2d_values, f3d_values, true); + } + for (const auto& i2d : bout::globals::mesh->getRegion2D("RGN_NOBNDRY")) { + fill_vector_values_op(i2d, option_data, p, f2d_values, f3d_values, false); + } + } + + /// Fill the IDA ``id`` vector with ``1`` for differential variables and ``0`` + /// for constrained algebraic variables. + void fill_id_vector(N_Vector vec) const { + ensure_manyvector_available(); + + if (use_manyvector()) { +#if BOUT_HAS_SUNDIALS_MANYVECTOR + std::size_t i = 0; + for (const auto& field : solver.f2d) { + fill_field(BoutNVector::get(vec, i), field.constraint ? 0.0 : 1.0, + field.evolve_bndry); + ++i; + } + for (const auto& field : solver.f3d) { + fill_field(BoutNVector::get(vec, i), field.constraint ? 0.0 : 1.0, + field.evolve_bndry); + ++i; + } + return; +#endif + } + + solver.set_id(N_VGetArrayPointer(vec)); + } + + /// Apply the IDA residual update ``residual -= du`` only on differential + /// components selected by ``id``. + void subtract_differential_term(N_Vector residual, N_Vector du, N_Vector id) const { + ensure_manyvector_available(); + + if (use_manyvector()) { +#if BOUT_HAS_SUNDIALS_MANYVECTOR + std::size_t i = 0; + for (const auto& field : solver.f2d) { + subtract_field(BoutNVector::get(residual, i), + BoutNVector::get(du, i), BoutNVector::get(id, i), + field.evolve_bndry); + ++i; + } + for (const auto& field : solver.f3d) { + subtract_field(BoutNVector::get(residual, i), + BoutNVector::get(du, i), BoutNVector::get(id, i), + field.evolve_bndry); + ++i; + } + return; +#endif + } + + const auto length = N_VGetLocalLength_Parallel(id); + const BoutReal* id_data = N_VGetArrayPointer(id); + BoutReal* residual_data = N_VGetArrayPointer(residual); + const BoutReal* du_data = N_VGetArrayPointer(du); + for (int i = 0; i < length; ++i) { + if (id_data[i] > 0.5) { + residual_data[i] -= du_data[i]; + } + } + } + +private: + Solver& solver; + const sundials::Context& ctx; + NVectorType nvector_type; + + void fill_vector_values_op(Ind2D UNUSED(i2d), BoutReal* option_data, int& p, + const std::vector& f2d_values, + const std::vector& f3d_values, bool bndry) const { + for (std::size_t i = 0; i < f2d_values.size(); ++i) { + if (bndry && !solver.f2d[i].evolve_bndry) { + continue; + } + option_data[p] = f2d_values[i]; + ++p; + } + + for (int jz = 0; jz < bout::globals::mesh->LocalNz; ++jz) { + for (std::size_t i = 0; i < f3d_values.size(); ++i) { + if (bndry && !solver.f3d[i].evolve_bndry) { + continue; + } + option_data[p] = f3d_values[i]; + ++p; + } + } + } + +#if BOUT_HAS_SUNDIALS_MANYVECTOR + /// Build a field-backed ManyVector that aliases the solver state variables. + N_Vector nvector_from_state() const { + std::vector subvectors; + subvectors.reserve(solver.f2d.size() + solver.f3d.size()); + const auto inserter = std::back_inserter(subvectors); + + const auto field_to_nvector = [this](const auto& var_str) { + return BoutNVector::create(ctx, *var_str.var, var_str.evolve_bndry); + }; + std::transform(solver.f2d.cbegin(), solver.f2d.cend(), inserter, field_to_nvector); + std::transform(solver.f3d.cbegin(), solver.f3d.cend(), inserter, field_to_nvector); + return BoutNVector::create(ctx, subvectors); + } + + /// Swap the active solver state fields with the subvectors contained in ``u``. + void swap_state(const N_Vector u) const { + std::size_t i = 0; + for (auto& var_str : solver.f2d) { + BoutNVector::swap(u, *var_str.var, i); + ++i; + } + for (auto& var_str : solver.f3d) { + BoutNVector::swap(u, *var_str.var, i); + ++i; + } + } + + /// Swap the active solver derivative fields with the subvectors in ``du``. + void swap_deriv(const N_Vector du) const { + std::size_t i = 0; + for (auto& var_str : solver.f2d) { + BoutNVector::swap(du, *var_str.F_var, i); + ++i; + } + for (auto& var_str : solver.f3d) { + BoutNVector::swap(du, *var_str.F_var, i); + ++i; + } + } + + template + static void fill_field(FieldType& field, BoutReal value, bool evolve_bndry) { + BOUT_FOR(i, field.getRegion(evolve_bndry ? RGN_ALL : RGN_NOBNDRY)) { + field[i] = value; + } + } + + template + static void subtract_field(FieldType& residual, const FieldType& du, + const FieldType& id, bool evolve_bndry) { + BOUT_FOR(i, residual.getRegion(evolve_bndry ? RGN_ALL : RGN_NOBNDRY)) { + if (id[i] > 0.5) { + residual[i] -= du[i]; + } + } + } +#endif +}; + +#endif diff --git a/src/sys/adios_object.cxx b/src/sys/adios_object.cxx index 53d8ccaa84..1b3e0a12e2 100644 --- a/src/sys/adios_object.cxx +++ b/src/sys/adios_object.cxx @@ -5,8 +5,8 @@ #include "bout/adios_object.hxx" #include "bout/boutexception.hxx" -#include -#include +#include + #include namespace bout { @@ -48,25 +48,25 @@ IOPtr GetIOPtr(const std::string IOName) { } ADIOSStream::~ADIOSStream() { - if (engine) { + if (engine_) { if (isInStep) { - engine.EndStep(); + engine_.EndStep(); isInStep = false; } - engine.Close(); + engine_.Close(); } } -ADIOSStream& ADIOSStream::ADIOSGetStream(const std::string& fname) { +ADIOSStream& ADIOSStream::ADIOSGetStream(const std::string& fname, adios2::Mode mode) { auto it = adiosStreams.find(fname); if (it == adiosStreams.end()) { - it = adiosStreams.emplace(fname, ADIOSStream(fname)).first; + it = adiosStreams.emplace(fname, ADIOSStream(fname, mode)).first; } return it->second; } -void ADIOSSetParameters(const std::string& input, const char delimKeyValue, - const char delimItem, adios2::IO& io) { +void ADIOSSetParameters(const std::string& input, char delimKeyValue, char delimItem, + adios2::IO& io) { auto lf_Trim = [](std::string& input) { input.erase(0, input.find_first_not_of(" \n\r\t")); // prefixing spaces input.erase(input.find_last_not_of(" \n\r\t") + 1); // suffixing spaces @@ -76,7 +76,7 @@ void ADIOSSetParameters(const std::string& input, const char delimKeyValue, std::string parameter; while (std::getline(inputSS, parameter, delimItem)) { const size_t position = parameter.find(delimKeyValue); - if (position == parameter.npos) { + if (position == std::string::npos) { throw BoutException("ADIOSSetParameters(): wrong format for IO parameter " + parameter + ", format must be key" + delimKeyValue + "value for each entry"); diff --git a/src/sys/bout_types.cxx b/src/sys/bout_types.cxx index 35d37f11be..b27fab7ae7 100644 --- a/src/sys/bout_types.cxx +++ b/src/sys/bout_types.cxx @@ -1,12 +1,12 @@ #include #include -#include + #include namespace { template const std::string& safeAt(const std::map& mymap, T t) { - AUTO_TRACE(); + auto found = mymap.find(t); if (found == mymap.end()) { throw BoutException("Did not find enum {:d}", static_cast(t)); @@ -16,7 +16,7 @@ const std::string& safeAt(const std::map& mymap, T t) { template const T& safeAt(const std::map& mymap, const std::string& s) { - AUTO_TRACE(); + auto found = mymap.find(s); if (found == mymap.end()) { throw BoutException("Did not find enum {:s}", s); @@ -26,7 +26,7 @@ const T& safeAt(const std::map& mymap, const std::string& s) { } // namespace std::string toString(CELL_LOC location) { - AUTO_TRACE(); + const static std::map CELL_LOCtoString = { ENUMSTR(CELL_DEFAULT), ENUMSTR(CELL_CENTRE), ENUMSTR(CELL_XLOW), ENUMSTR(CELL_YLOW), ENUMSTR(CELL_ZLOW), ENUMSTR(CELL_VSHIFT)}; @@ -35,7 +35,7 @@ std::string toString(CELL_LOC location) { } CELL_LOC CELL_LOCFromString(const std::string& location_string) { - AUTO_TRACE(); + const static std::map stringtoCELL_LOC = { STRENUM(CELL_DEFAULT), STRENUM(CELL_CENTRE), STRENUM(CELL_XLOW), STRENUM(CELL_YLOW), STRENUM(CELL_ZLOW), STRENUM(CELL_VSHIFT)}; @@ -44,7 +44,7 @@ CELL_LOC CELL_LOCFromString(const std::string& location_string) { } std::string toString(DIFF_METHOD location) { - AUTO_TRACE(); + const static std::map DIFF_METHODtoString = { {DIFF_DEFAULT, "DEFAULT"}, {DIFF_U1, "U1"}, {DIFF_U2, "U2"}, {DIFF_U3, "U3"}, {DIFF_C2, "C2"}, {DIFF_C4, "C4"}, {DIFF_S2, "S2"}, {DIFF_W2, "W2"}, @@ -54,7 +54,7 @@ std::string toString(DIFF_METHOD location) { } std::string toString(REGION region) { - AUTO_TRACE(); + const static std::map REGIONtoString = { ENUMSTR(RGN_ALL), ENUMSTR(RGN_NOBNDRY), ENUMSTR(RGN_NOX), ENUMSTR(RGN_NOY), ENUMSTR(RGN_NOZ)}; @@ -62,7 +62,7 @@ std::string toString(REGION region) { } std::string toString(DIRECTION direction) { - AUTO_TRACE(); + const static std::map DIRECTIONtoString = { {DIRECTION::X, "X"}, {DIRECTION::Y, "Y"}, @@ -116,7 +116,7 @@ bool areDirectionsCompatible(const DirectionTypes& d1, const DirectionTypes& d2) } std::string toString(STAGGER stagger) { - AUTO_TRACE(); + const static std::map STAGGERtoString = { {STAGGER::None, "No staggering"}, {STAGGER::C2L, "Centre to Low"}, @@ -126,7 +126,7 @@ std::string toString(STAGGER stagger) { } std::string toString(DERIV deriv) { - AUTO_TRACE(); + const static std::map DERIVtoString = { {DERIV::Standard, "Standard"}, {DERIV::StandardSecond, "Standard -- second order"}, @@ -138,7 +138,7 @@ std::string toString(DERIV deriv) { } std::string toString(YDirectionType d) { - AUTO_TRACE(); + const static std::map YDirectionTypeToString = { {YDirectionType::Standard, "Standard"}, {YDirectionType::Aligned, "Aligned"}}; @@ -146,7 +146,7 @@ std::string toString(YDirectionType d) { } YDirectionType YDirectionTypeFromString(const std::string& y_direction_string) { - AUTO_TRACE(); + const static std::map stringToYDirectionType = { {"Standard", YDirectionType::Standard}, {"Aligned", YDirectionType::Aligned}}; @@ -154,7 +154,7 @@ YDirectionType YDirectionTypeFromString(const std::string& y_direction_string) { } std::string toString(ZDirectionType d) { - AUTO_TRACE(); + const static std::map ZDirectionTypeToString = { {ZDirectionType::Standard, "Standard"}, {ZDirectionType::Average, "Average"}}; @@ -162,7 +162,7 @@ std::string toString(ZDirectionType d) { } ZDirectionType ZDirectionTypeFromString(const std::string& z_direction_string) { - AUTO_TRACE(); + const static std::map stringToZDirectionType = { {"Standard", ZDirectionType::Standard}, {"Average", ZDirectionType::Average}}; diff --git a/src/sys/boutcomm.cxx b/src/sys/boutcomm.cxx index 2d78a24076..358f2346f9 100644 --- a/src/sys/boutcomm.cxx +++ b/src/sys/boutcomm.cxx @@ -58,6 +58,8 @@ int BoutComm::size() { return NPES; } +void BoutComm::abort(int errorcode) { MPI_Abort(get(), errorcode); } + BoutComm* BoutComm::getInstance() { if (instance == nullptr) { // Create the singleton object diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index 825fb37e28..eff2de2e1f 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -1,39 +1,38 @@ -#include "bout/build_defines.hxx" - #include #include #include -#include + #include -#if BOUT_USE_BACKTRACE -#include -#include -#endif +#include // IWYU pragma: keep +#include +#include -#include #include #include #include -namespace { -const std::string header{"====== Exception thrown ======\n"}; -} +bool BoutException::show_backtrace = true; void BoutParallelThrowRhsFail(int status, const char* message) { - int allstatus; + int allstatus = 0; MPI_Allreduce(&status, &allstatus, 1, MPI_INT, MPI_LOR, BoutComm::get()); - if (allstatus) { + if (allstatus != 0) { throw BoutRhsFail(message); } } BoutException::BoutException(std::string msg) : message(std::move(msg)) { - makeBacktrace(); - if (std::getenv("BOUT_SHOW_BACKTRACE") != nullptr) { - message = getBacktrace() + "\n" + message; + const char* show_backtrace_env_var = std::getenv("BOUT_SHOW_BACKTRACE"); + const auto should_show_backtrace = + show_backtrace + and ((show_backtrace_env_var == nullptr) + or (show_backtrace_env_var != nullptr + and std::string{show_backtrace_env_var} != "0")); + if (should_show_backtrace) { + message = getBacktrace(); } } @@ -42,67 +41,36 @@ BoutException::~BoutException() { // up the msg_stack. We also won't know how many messages to pop, so // just clear everything msg_stack.clear(); -#if BOUT_USE_BACKTRACE - // Call required for memory allocated by `backtrace_symbols` - free(messages); // NOLINT -#endif } std::string BoutException::getBacktrace() const { - std::string backtrace_message; -#if BOUT_USE_BACKTRACE - backtrace_message = "====== Exception path ======\n"; - // skip first stack frame (points here) - for (int i = trace_size - 1; i > 1; --i) { - backtrace_message += fmt::format(FMT_STRING("[bt] #{:d} {:s}\n"), i - 1, messages[i]); - // find first occurence of '(' or ' ' in message[i] and assume - // everything before that is the file name. (Don't go beyond 0 though - // (string terminator) - int p = 0; // snprintf %.*s expects int - while (messages[i][p] != '(' && messages[i][p] != ' ' && messages[i][p] != 0) { - ++p; - } + using namespace cpptrace; - // If we are compiled as PIE, need to get base pointer of .so and substract - Dl_info info; - void* ptr = trace[i]; - if (dladdr(trace[i], &info)) { - // Additionally, check whether this is the default offset for an executable - if (info.dli_fbase != reinterpret_cast(0x400000)) { - ptr = reinterpret_cast(reinterpret_cast(trace[i]) - - reinterpret_cast(info.dli_fbase)); - } - } + const auto colours = isatty(stdout_fileno) || isatty(stderr_fileno) + ? formatter::color_mode::always + : formatter::color_mode::none; - // Pipe stderr to /dev/null to avoid cluttering output - // when addr2line fails or is not installed - const auto syscom = fmt::format( - FMT_STRING("addr2line {:p} -Cfpie {:.{}s} 2> /dev/null"), ptr, messages[i], p); - // last parameter is the file name of the symbol - FILE* file = popen(syscom.c_str(), "r"); - if (file != nullptr) { - std::array out{}; - char* retstr = nullptr; - std::string buf; - while ((retstr = fgets(out.data(), out.size() - 1, file)) != nullptr) { - buf += retstr; - } - int const status = pclose(file); - if (status == 0) { - backtrace_message += buf; - } - } - } -#else - backtrace_message = "Stacktrace not enabled.\n"; -#endif + auto formatter = cpptrace::formatter{} + .addresses(formatter::address_mode::none) + .break_before_filename(true) + .colors(colours) + .snippets(true) + .symbols(formatter::symbol_mode::pretty) + .filter([](const stacktrace_frame& frame) { + return ( + // Don't include our exception machinery + (frame.symbol.find("BoutException::") == std::string::npos) + // Don't include pre-main functions + and (frame.symbol.find("__libc_start") == std::string::npos) + and (frame.symbol != "_start")); + }); - return backtrace_message + msg_stack.getDump() + "\n" + header + message + "\n"; -} + std::string backtrace_message = + fmt::format("{}\n\n====== Exception thrown ======\n{}", + formatter.format(generate_trace()), message); -void BoutException::makeBacktrace() { -#if BOUT_USE_BACKTRACE - trace_size = backtrace(trace.data(), TRACE_MAX); - messages = backtrace_symbols(trace.data(), trace_size); -#endif + if (msg_stack.size() > 0) { + return fmt::format("{}\n\n{}", backtrace_message, msg_stack.getDump()); + } + return backtrace_message; } diff --git a/src/sys/derivs.cxx b/src/sys/derivs.cxx index ee9bcbcc2c..e449dbcd30 100644 --- a/src/sys/derivs.cxx +++ b/src/sys/derivs.cxx @@ -17,9 +17,9 @@ * Div(v*f) * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -38,19 +38,22 @@ * **************************************************************************/ +#include +#include +#include #include +#include #include #include +#include +#include #include #include -#include -#include -#include - -#include - #include #include +#include + +#include /******************************************************************************* * First central derivatives @@ -70,7 +73,7 @@ Coordinates::FieldMetric DDX(const Field2D& f, CELL_LOC outloc, const std::strin ////////////// Y DERIVATIVE ///////////////// -Field3D DDY(const Field3D& f, CELL_LOC outloc, const std::string& method, +Field3D DDY(const Field3DParallel& f, CELL_LOC outloc, const std::string& method, const std::string& region) { return bout::derivatives::index::DDY(f, outloc, method, region) / f.getCoordinates(outloc)->dy; @@ -100,7 +103,7 @@ Coordinates::FieldMetric DDZ(const Field2D& f, CELL_LOC UNUSED(outloc), Vector3D DDZ(const Vector3D& v, CELL_LOC outloc, const std::string& method, const std::string& region) { Vector3D result(v.getMesh()); - Coordinates* metric = v.x.getCoordinates(outloc); + const Coordinates* metric = v.x.getCoordinates(outloc); if (v.covariant) { // From equation (2.6.32) in D'Haeseleer @@ -152,7 +155,7 @@ Vector2D DDZ(const Vector2D& v, CELL_LOC UNUSED(outloc), Field3D D2DX2(const Field3D& f, CELL_LOC outloc, const std::string& method, const std::string& region) { - Coordinates* coords = f.getCoordinates(outloc); + const Coordinates* coords = f.getCoordinates(outloc); Field3D result = bout::derivatives::index::D2DX2(f, outloc, method, region) / SQ(coords->dx); @@ -171,9 +174,9 @@ Field3D D2DX2(const Field3D& f, CELL_LOC outloc, const std::string& method, Coordinates::FieldMetric D2DX2(const Field2D& f, CELL_LOC outloc, const std::string& method, const std::string& region) { - Coordinates* coords = f.getCoordinates(outloc); + const Coordinates* coords = f.getCoordinates(outloc); - auto result = + Coordinates::FieldMetric result = bout::derivatives::index::D2DX2(f, outloc, method, region) / SQ(coords->dx); if (coords->non_uniform) { @@ -189,7 +192,7 @@ Coordinates::FieldMetric D2DX2(const Field2D& f, CELL_LOC outloc, Field3D D2DY2(const Field3D& f, CELL_LOC outloc, const std::string& method, const std::string& region) { - Coordinates* coords = f.getCoordinates(outloc); + const Coordinates* coords = f.getCoordinates(outloc); Field3D result = bout::derivatives::index::D2DY2(f, outloc, method, region) / SQ(coords->dy); @@ -208,9 +211,9 @@ Field3D D2DY2(const Field3D& f, CELL_LOC outloc, const std::string& method, Coordinates::FieldMetric D2DY2(const Field2D& f, CELL_LOC outloc, const std::string& method, const std::string& region) { - Coordinates* coords = f.getCoordinates(outloc); + const Coordinates* coords = f.getCoordinates(outloc); - auto result = + Coordinates::FieldMetric result = bout::derivatives::index::D2DY2(f, outloc, method, region) / SQ(coords->dy); if (coords->non_uniform) { // Correction for non-uniform f.getMesh() @@ -290,7 +293,7 @@ Coordinates::FieldMetric D2DXDY(const Field2D& f, CELL_LOC outloc, const std::string& method, const std::string& region, const std::string& dfdy_boundary_condition, const std::string& dfdy_region) { - std::string dy_region = dfdy_region.empty() ? region : dfdy_region; + const std::string dy_region = dfdy_region.empty() ? region : dfdy_region; // If staggering in x, take y-derivative at f's location. const auto y_location = @@ -315,7 +318,7 @@ Coordinates::FieldMetric D2DXDY(const Field2D& f, CELL_LOC outloc, Field3D D2DXDY(const Field3D& f, CELL_LOC outloc, const std::string& method, const std::string& region, const std::string& dfdy_boundary_condition, const std::string& dfdy_region) { - std::string dy_region = dfdy_region.empty() ? region : dfdy_region; + const std::string dy_region = dfdy_region.empty() ? region : dfdy_region; // If staggering in x, take y-derivative at f's location. const auto y_location = @@ -410,7 +413,7 @@ Coordinates::FieldMetric VDDY(const Field2D& v, const Field2D& f, CELL_LOC outlo } // general case -Field3D VDDY(const Field3D& v, const Field3D& f, CELL_LOC outloc, +Field3D VDDY(const Field3D& v, const Field3DParallel& f, CELL_LOC outloc, const std::string& method, const std::string& region) { return bout::derivatives::index::VDDY(v, f, outloc, method, region) / f.getCoordinates(outloc)->dy; @@ -471,7 +474,7 @@ Coordinates::FieldMetric FDDY(const Field2D& v, const Field2D& f, CELL_LOC outlo / f.getCoordinates(outloc)->dy; } -Field3D FDDY(const Field3D& v, const Field3D& f, CELL_LOC outloc, +Field3D FDDY(const Field3D& v, const Field3DParallel& f, CELL_LOC outloc, const std::string& method, const std::string& region) { return bout::derivatives::index::FDDY(v, f, outloc, method, region) / f.getCoordinates(outloc)->dy; diff --git a/src/sys/expressionparser.cxx b/src/sys/expressionparser.cxx index 8290a4cae0..5573846c4f 100644 --- a/src/sys/expressionparser.cxx +++ b/src/sys/expressionparser.cxx @@ -22,12 +22,29 @@ * **************************************************************************/ -#include - +#include "bout/sys/expressionparser.hxx" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include - -#include "bout/sys/gettext.hxx" -#include "bout/utils.hxx" +#include using std::list; using std::string; @@ -37,12 +54,6 @@ using namespace std::string_literals; using bout::generator::Context; -// Note: Here rather than in header to avoid many deprecated warnings -// Remove in future and make this function pure virtual -double FieldGenerator::generate(const Context& ctx) { - return generate(ctx.x(), ctx.y(), ctx.z(), ctx.t()); -} - ///////////////////////////////////////////// namespace { // These classes only visible in this file @@ -189,7 +200,7 @@ FieldGeneratorPtr FieldBinary::clone(const list args) { bool toBool(BoutReal rval) { int ival = ROUND(rval); if ((fabs(rval - static_cast(ival)) > 1e-3) or (ival < 0) or (ival > 1)) { - throw BoutException(_("Boolean operator argument {:e} is not a bool"), rval); + throw BoutException(_f("Boolean operator argument {:e} is not a bool"), rval); } return ival == 1; } @@ -308,11 +319,6 @@ ExpressionParser::fuzzyFind(const std::string& name, FieldGeneratorPtr ExpressionParser::parseIdentifierExpr(LexInfo& lex) const { // Make a nice error message if we couldn't find the identifier const auto generatorNotFoundErrorMessage = [&](const std::string& name) -> std::string { - const std::string message_template = _( - R"(Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so you -may need to change your input file. -{})"); - // Start position of the current identifier: by this point, we've either // moved one character past the token, or we're still at the start const auto start = @@ -332,13 +338,18 @@ may need to change your input file. [](const auto& match) -> bool { return match.distance == 0; }); // No matches, just point out the error + std::string error_message = fmt::format( + _f( + R"(Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so you +may need to change your input file. +{})"), + name, problem_bit); if (possible_matches.empty()) { - return fmt::format(message_template, name, problem_bit); + return error_message; } // Give the first suggestion as a possible alternative - std::string error_message = fmt::format(message_template, name, problem_bit); - error_message += fmt::format(_("\n {1: ^{2}}{0}\n Did you mean '{0}'?"), + error_message += fmt::format(_f("\n {1: ^{2}}{0}\n Did you mean '{0}'?"), possible_matches.begin()->name, "", start); return error_message; }; diff --git a/src/sys/generator_context.cxx b/src/sys/generator_context.cxx index 25274e8107..31a5662378 100644 --- a/src/sys/generator_context.cxx +++ b/src/sys/generator_context.cxx @@ -1,5 +1,7 @@ #include "bout/sys/generator_context.hxx" + #include "bout/boundary_region.hxx" +#include "bout/bout_types.hxx" #include "bout/constants.hxx" #include "bout/mesh.hxx" @@ -15,9 +17,8 @@ Context::Context(int ix, int iy, int iz, CELL_LOC loc, Mesh* msh, BoutReal t) parameters["y"] = (loc == CELL_YLOW) ? PI * (msh->GlobalY(iy) + msh->GlobalY(iy - 1)) : TWOPI * msh->GlobalY(iy); - parameters["z"] = (loc == CELL_ZLOW) - ? TWOPI * (iz - 0.5) / static_cast(msh->LocalNz) - : TWOPI * iz / static_cast(msh->LocalNz); + parameters["z"] = (loc == CELL_ZLOW) ? PI * (msh->GlobalZ(iz) + msh->GlobalZ(iz - 1)) + : TWOPI * msh->GlobalZ(iz); parameters["t"] = t; } @@ -26,21 +27,20 @@ Context::Context(const BoundaryRegion* bndry, int iz, CELL_LOC loc, BoutReal t, : localmesh(msh) { // Add one to X index if boundary is in -x direction, so that XLOW is on the boundary - int ix = (bndry->bx < 0) ? bndry->x + 1 : bndry->x; + const int ix = (bndry->bx < 0) ? bndry->x + 1 : bndry->x; parameters["x"] = ((loc == CELL_XLOW) || (bndry->bx != 0)) ? 0.5 * (msh->GlobalX(ix) + msh->GlobalX(ix - 1)) : msh->GlobalX(ix); - int iy = (bndry->by < 0) ? bndry->y + 1 : bndry->y; + const int iy = (bndry->by < 0) ? bndry->y + 1 : bndry->y; - parameters["y"] = ((loc == CELL_YLOW) || bndry->by) + parameters["y"] = ((loc == CELL_YLOW) || (bndry->by != 0)) ? PI * (msh->GlobalY(iy) + msh->GlobalY(iy - 1)) : TWOPI * msh->GlobalY(iy); - parameters["z"] = (loc == CELL_ZLOW) - ? TWOPI * (iz - 0.5) / static_cast(msh->LocalNz) - : TWOPI * iz / static_cast(msh->LocalNz); + parameters["z"] = (loc == CELL_ZLOW) ? PI * (msh->GlobalZ(iz) + msh->GlobalZ(iz - 1)) + : TWOPI * msh->GlobalZ(iz); parameters["t"] = t; } diff --git a/src/sys/msg_stack.cxx b/src/sys/msg_stack.cxx index 502836324c..3dbd7c2797 100644 --- a/src/sys/msg_stack.cxx +++ b/src/sys/msg_stack.cxx @@ -27,7 +27,7 @@ #include "bout/openmpwrap.hxx" #include #include -#include + #include #if BOUT_USE_OPENMP @@ -91,7 +91,7 @@ void MsgStack::dump() { } std::string MsgStack::getDump() { - std::string res = "====== Back trace ======\n"; + std::string res = "=== Additional information ===\n"; for (int i = position - 1; i >= 0; i--) { if (stack[i] != "") { res += " -> "; diff --git a/src/sys/options.cxx b/src/sys/options.cxx index 24c9e933c8..85cfa7a49a 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -106,13 +107,11 @@ Options::Options(InitializerList values, Options* parent_instance, } Options& Options::operator[](const std::string& name) { - TRACE("Options::operator[]"); - if (isValue()) { - throw BoutException(_("Trying to index Option '{0}' with '{1}', but '{0}' is a " - "value, not a section.\n" - "This is likely the result of clashing input options, and you " - "may have to rename one of them.\n"), + throw BoutException(_f("Trying to index Option '{0}' with '{1}', but '{0}' is a " + "value, not a section.\n" + "This is likely the result of clashing input options, and you " + "may have to rename one of them.\n"), full_name, name); } @@ -145,13 +144,11 @@ Options& Options::operator[](const std::string& name) { } const Options& Options::operator[](const std::string& name) const { - TRACE("Options::operator[] const"); - if (isValue()) { - throw BoutException(_("Trying to index Option '{0}' with '{1}', but '{0}' is a " - "value, not a section.\n" - "This is likely the result of clashing input options, and you " - "may have to rename one of them.\n"), + throw BoutException(_f("Trying to index Option '{0}' with '{1}', but '{0}' is a " + "value, not a section.\n" + "This is likely the result of clashing input options, and you " + "may have to rename one of them.\n"), full_name, name); } @@ -169,7 +166,7 @@ const Options& Options::operator[](const std::string& name) const { auto child = children.find(name); if (child == children.end()) { // Doesn't exist - throw BoutException(_("Option {:s}:{:s} does not exist"), full_name, name); + throw BoutException(_f("Option {:s}:{:s} does not exist"), full_name, name); } return child->second; @@ -333,15 +330,55 @@ Options& Options::assign<>(Array val, std::string source) { return *this; } template <> +Options& Options::assign<>(Array val, std::string source) { + _set_no_check(std::move(val), std::move(source)); + return *this; +} +template <> Options& Options::assign<>(Matrix val, std::string source) { _set_no_check(std::move(val), std::move(source)); return *this; } template <> +Options& Options::assign<>(Matrix val, std::string source) { + _set_no_check(std::move(val), std::move(source)); + return *this; +} +template <> Options& Options::assign<>(Tensor val, std::string source) { _set_no_check(std::move(val), std::move(source)); return *this; } +template <> +Options& Options::assign<>(Tensor val, std::string source) { + _set_no_check(std::move(val), std::move(source)); + return *this; +} + +void saveParallel(Options& opt, const std::string& name, const Field3D& tosave) { + opt[name] = tosave; + const size_t numberParallelSlices = + tosave.hasParallelSlices() ? 0 : tosave.getMesh()->ystart; + for (size_t i0 = 1; i0 <= numberParallelSlices; ++i0) { + for (int i : {i0, -i0}) { + Field3D tmp; + tmp.allocate(); + const auto& fpar = tosave.ynext(i); + if (fpar.isAllocated()) { + for (auto j : tmp.getRegion("RGN_NOY")) { + tmp[j] = fpar[j.yp(i)]; + } + opt[fmt::format("{}_y{:+d}", name, i)] = tmp; + } else { + if (tosave.isFci()) { // likely an error + throw BoutException( + "Tried to save parallel fields - but parallel field {} is not allocated", + i); + } + } + } + } +} namespace { /// Use FieldFactory to evaluate expression @@ -358,7 +395,7 @@ double parseExpression(const Options::ValueType& value, const Options* options, return gen->generate({}); } catch (ParseException& error) { // Convert any exceptions to something a bit more useful - throw BoutException(_("Couldn't get {} from option {:s} = '{:s}': {}"), type, + throw BoutException(_f("Couldn't get {} from option {:s} = '{:s}': {}"), type, full_name, bout::utils::variantToString(value), error.what()); } } @@ -366,7 +403,7 @@ double parseExpression(const Options::ValueType& value, const Options* options, /// Helper function to print `key = value` with optional source template void printNameValueSourceLine(const Options& option, const T& value) { - output_info.write(_("\tOption {} = {}"), option.str(), value); + output_info.write(_f("\tOption {} = {}"), option.str(), value); if (option.hasAttribute("source")) { // Specify the source of the setting output_info.write(" ({})", @@ -379,7 +416,7 @@ void printNameValueSourceLine(const Options& option, const T& value) { template <> std::string Options::as(const std::string& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } // Mark this option as used @@ -395,7 +432,7 @@ std::string Options::as(const std::string& UNUSED(similar_to)) cons template <> int Options::as(const int& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } int result = 0; @@ -415,7 +452,7 @@ int Options::as(const int& UNUSED(similar_to)) const { } else { // Another type which can't be converted - throw BoutException(_("Value for option {:s} is not an integer"), full_name); + throw BoutException(_f("Value for option {:s} is not an integer"), full_name); } // Convert to int by rounding @@ -423,7 +460,7 @@ int Options::as(const int& UNUSED(similar_to)) const { // Check that the value is close to an integer if (fabs(rval - static_cast(result)) > 1e-3) { - throw BoutException(_("Value for option {:s} = {:e} is not an integer"), full_name, + throw BoutException(_f("Value for option {:s} = {:e} is not an integer"), full_name, rval); } } @@ -438,7 +475,7 @@ int Options::as(const int& UNUSED(similar_to)) const { template <> BoutReal Options::as(const BoutReal& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } BoutReal result = BoutNaN; @@ -453,7 +490,7 @@ BoutReal Options::as(const BoutReal& UNUSED(similar_to)) const { result = parseExpression(value, this, "BoutReal", full_name); } else { - throw BoutException(_("Value for option {:s} cannot be converted to a BoutReal"), + throw BoutException(_f("Value for option {:s} cannot be converted to a BoutReal"), full_name); } @@ -468,7 +505,7 @@ BoutReal Options::as(const BoutReal& UNUSED(similar_to)) const { template <> bool Options::as(const bool& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } bool result = false; @@ -483,12 +520,12 @@ bool Options::as(const bool& UNUSED(similar_to)) const { // Check that the result is either close to 1 (true) or close to 0 (false) const int ival = ROUND(rval); if ((fabs(rval - static_cast(ival)) > 1e-3) or (ival < 0) or (ival > 1)) { - throw BoutException(_("Value for option {:s} = {:e} is not a bool"), full_name, + throw BoutException(_f("Value for option {:s} = {:e} is not a bool"), full_name, rval); } result = ival == 1; } else { - throw BoutException(_("Value for option {:s} cannot be converted to a bool"), + throw BoutException(_f("Value for option {:s} cannot be converted to a bool"), full_name); } @@ -568,7 +605,7 @@ Field3D Options::as(const Field3D& similar_to) const { localmesh->LocalNz); } - throw BoutException(_("Value for option {:s} cannot be converted to a Field3D"), + throw BoutException(_f("Value for option {:s} cannot be converted to a Field3D"), full_name); } @@ -620,7 +657,7 @@ Field2D Options::as(const Field2D& similar_to) const { } } - throw BoutException(_("Value for option {:s} cannot be converted to a Field2D"), + throw BoutException(_f("Value for option {:s} cannot be converted to a Field2D"), full_name); } @@ -702,32 +739,51 @@ FieldPerp Options::as(const FieldPerp& similar_to) const { // to select a region from it using Mesh e.g. if this // is from the input grid file. } - throw BoutException(_("Value for option {:s} cannot be converted to a FieldPerp"), + throw BoutException(_f("Value for option {:s} cannot be converted to a FieldPerp"), full_name); } namespace { -/// Visitor to convert an int, BoutReal or Array/Matrix/Tensor to the -/// appropriate container +/// Primary declaration of ConvertContainer, for specialization below. +/// No definition needed unless it is used. template -struct ConvertContainer { +struct ConvertContainer; + +/// Visitor to convert an int, BoutReal or Array/Matrix/Tensor to the +/// appropriate container. Templated on both the container class C +/// and scalar type Scalar. +template