diff --git a/.github/workflows/add_release_documentation.yml b/.github/workflows/add_release_documentation.yml index be9fd17f94..39643b46bb 100644 --- a/.github/workflows/add_release_documentation.yml +++ b/.github/workflows/add_release_documentation.yml @@ -93,7 +93,7 @@ jobs: && echo CXXFLAGS_var="-Wall -pedantic -O3" >> $GITHUB_ENV - name: build config variables if: ${{ env.MINOR_RELEASE == 'true' }} - run: export CONFIG_OPTIONS="--without-blas ${LESS_TEST_OPTION}" + run: export CONFIG_OPTIONS="--without-blas ${TEST_LEVEL_FLAG}" && export CONFIG_SERIAL_DEBUG="$CONFIG_OPTIONS --enable-debug --with-sc=$SC_SERIAL_DEBUG/install --with-p4est=$P4EST_SERIAL_DEBUG/install" - name: Check vars if: ${{ env.MINOR_RELEASE == 'true' }} diff --git a/.github/workflows/build_cmake_tarball.yml b/.github/workflows/build_cmake_tarball.yml index d5db4e723a..306b384a30 100644 --- a/.github/workflows/build_cmake_tarball.yml +++ b/.github/workflows/build_cmake_tarball.yml @@ -32,16 +32,16 @@ env: on: workflow_call: inputs: - LESS_TESTS: + TEST_LEVEL: required: true - type: boolean - description: 'Enable less tests option for configuring' + type: number + description: 'Test level used for configuring (0 for full tests, 1 for less thorough tests, 2 for minimal tests)' jobs: build: if: (github.event_name == 'schedule' && github.repository == 'DLR-AMR/t8code') || (github.event_name != 'schedule') - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest container: dlramr/t8code-ubuntu:t8-dependencies timeout-minutes: 90 steps: @@ -59,8 +59,6 @@ jobs: uses: nikeee/setup-pandoc@v1 - name: Test pandoc run: pandoc --version - # On the github Ubuntu 20.04, sudo is not available by default - # we need it, however, to update/upgrade our packages. - name: Update packages run: sudo apt-get update && sudo apt-get upgrade -y # This step is necessary to get the newest package data @@ -77,7 +75,7 @@ jobs: # # - name: build config variables - run: export CONFIG_OPTIONS="${LESS_TEST_OPTION} -GNinja -DCMAKE_C_FLAGS_DEBUG=$DEBUG_FLAGS -DCMAKE_CXX_FLAGS_DEBUG=$DEBUG_FLAGS -DT8CODE_USE_SYSTEM_SC=OFF -DT8CODE_USE_SYSTEM_P4EST=OFF -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE" + run: export CONFIG_OPTIONS="${TEST_LEVEL_FLAG} -GNinja -DCMAKE_C_FLAGS_DEBUG=$DEBUG_FLAGS -DCMAKE_CXX_FLAGS_DEBUG=$DEBUG_FLAGS -DT8CODE_USE_SYSTEM_SC=OFF -DT8CODE_USE_SYSTEM_P4EST=OFF -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE" && echo CONFIG_OPTIONS="$CONFIG_OPTIONS" >> $GITHUB_ENV - name: cmake run: mkdir build && cd build && cmake ../ $CONFIG_OPTIONS @@ -99,14 +97,12 @@ jobs: test-tarball: needs: build - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest container: dlramr/t8code-ubuntu:t8-dependencies timeout-minutes: 90 steps: - name: install sudo run: apt update && apt install sudo - # On the github Ubuntu 20.04, sudo is not available by default - # we need it, however, to update/upgrade our packages. - name: Update packages run: sudo apt-get update && sudo apt-get upgrade -y # This step is necessary to get the newest package data @@ -122,12 +118,12 @@ jobs: echo TAR_DIR="$TAR_DIR" >>$GITHUB_ENV # build config vars - - name: less-test-option - if: ${{ inputs.LESS_TESTS }} - run: export LESS_TEST_OPTION="-DT8CODE_ENABLE_LESS_TESTS=ON" - && echo LESS_TEST_OPTION="$LESS_TEST_OPTION" >> $GITHUB_ENV + - name: Set test level + if: ${{ inputs.TEST_LEVEL != 0 }} + run: export TEST_LEVEL_FLAG="-DT8CODE_TEST_LEVEL=${{ inputs.TEST_LEVEL }}" + && echo TEST_LEVEL_FLAG="$TEST_LEVEL_FLAG" >> $GITHUB_ENV - name: build config variables - run: export CONFIG_OPTIONS="${LESS_TEST_OPTION} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_USE_SYSTEM_SC=OFF -DT8CODE_USE_SYSTEM_P4EST=OFF -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE" + run: export CONFIG_OPTIONS="${TEST_LEVEL_FLAG} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_USE_SYSTEM_SC=OFF -DT8CODE_USE_SYSTEM_P4EST=OFF -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE" && echo CONFIG_OPTIONS="$CONFIG_OPTIONS" >> $GITHUB_ENV - name: Check vars run: echo "[$CONFIG_DEBUG]" diff --git a/.github/workflows/check_indentation.yml b/.github/workflows/check_indentation.yml index 920ddcb609..592b4c5ee7 100644 --- a/.github/workflows/check_indentation.yml +++ b/.github/workflows/check_indentation.yml @@ -31,15 +31,11 @@ name: t8code indentation check # The output is uploaded as an artifact to the github page. on: + merge_group: push: branches: - main - - develop - - feature-*CI* # for testing this script, all feature branches with "CI" in their name pull_request: - branches: - - main - - develop workflow_dispatch: # Be able to trigger this manually on github.com # Run every night at 1:05 schedule: @@ -48,7 +44,7 @@ on: jobs: indent: if: (github.event_name == 'schedule' && github.repository == 'DLR-AMR/t8code') || (github.event_name != 'schedule') - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v4 @@ -58,8 +54,6 @@ jobs: # Deactivated the install sudo because it failed. #- name: install sudo # run: apt update && apt install sudo - # On the github Ubuntu 20.04, sudo is not available by default - # we need it, however, to update/upgrade our packages. - name: Update packages run: sudo apt-get update && sudo apt-get upgrade -y # This step is necessary to get the newest package data diff --git a/.github/workflows/mattermost_issue.yml b/.github/workflows/mattermost_issue.yml index a0126646b5..7c4f351c12 100644 --- a/.github/workflows/mattermost_issue.yml +++ b/.github/workflows/mattermost_issue.yml @@ -39,7 +39,7 @@ env: jobs: send_mm_message_issue: if: github.repository == 'DLR-AMR/t8code' - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: debug_before_build run: echo ${{ env.message_build}} diff --git a/.github/workflows/mattermost_pull_request.yml b/.github/workflows/mattermost_pull_request.yml index 97b78825c9..42c2e0fc73 100644 --- a/.github/workflows/mattermost_pull_request.yml +++ b/.github/workflows/mattermost_pull_request.yml @@ -28,7 +28,7 @@ name: Mattermost_message_pull_request on: # Triggers the workflow on pull request events on the feature-CI_mattermost_messages, develop or main branch pull_request: - branches: [ feature-CI_mattermost_messages, develop, main ] + branches: types: [ opened, synchronize, reopened, closed] # Allows you to run this workflow manually from the Actions tab @@ -39,7 +39,7 @@ env: jobs: send_mm_message_pr: if: github.repository == 'DLR-AMR/t8code' - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: debug_before_build run: echo ${{ env.message_build}} diff --git a/.github/workflows/spell_check.yml b/.github/workflows/spell_check.yml index 035aa4e7aa..4a48f11860 100644 --- a/.github/workflows/spell_check.yml +++ b/.github/workflows/spell_check.yml @@ -1,10 +1,10 @@ name: spell_check on: + merge_group: push: branches: - main - - feature-*CI* # for testing this script, all feature branches with "CI" in their name pull_request: workflow_dispatch: # Be able to trigger this manually on github.com @@ -17,3 +17,14 @@ jobs: uses: actions/checkout@v4 - name: Check spelling uses: crate-ci/typos@master + - name: check macros + run: | + for file in $(git diff --name-only --diff-filter=A); do + ./scripts/check_macros.scp "$file" &>> check_macros.txt + done + - name: Archive script output + if: failure() + uses: actions/upload-artifact@v4 + with: + name: t8code check macros report + path: check_macros.txt diff --git a/.github/workflows/tests_cmake_t8code.yml b/.github/workflows/tests_cmake_t8code.yml index 430fc1cd4c..18a8604f10 100644 --- a/.github/workflows/tests_cmake_t8code.yml +++ b/.github/workflows/tests_cmake_t8code.yml @@ -39,10 +39,10 @@ on: required: true type: string description: 'Build type (Release/Debug)' - LESS_TESTS: + TEST_LEVEL: required: true - type: boolean - description: 'Enable less tests option for configuring' + type: number + description: 'Test level used for configuring (0 for full tests, 1 for less thorough tests, 2 for minimal tests)' jobs: t8code_cmake_tests: @@ -79,12 +79,12 @@ jobs: # T8CODE # # build config vars - - name: less-test option - if: ${{ inputs.LESS_TESTS }} - run: export LESS_TEST_OPTION="-DT8CODE_ENABLE_LESS_TESTS=ON" - && echo LESS_TEST_OPTION="$LESS_TEST_OPTION" >> $GITHUB_ENV + - name: Set test level + if: ${{ inputs.TEST_LEVEL != 0 }} + run: export TEST_LEVEL_FLAG="-DT8CODE_TEST_LEVEL=${{ inputs.TEST_LEVEL }}" + && echo TEST_LEVEL_FLAG="$TEST_LEVEL_FLAG" >> $GITHUB_ENV - name: build config variables - run: export CONFIG_OPTIONS="${LESS_TEST_OPTION} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_USE_SYSTEM_SC=ON -DT8CODE_USE_SYSTEM_P4EST=ON -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSC_DIR=$SC_PATH/install/cmake -DP4EST_DIR=$P4EST_PATH/install/cmake" + run: export CONFIG_OPTIONS="${TEST_LEVEL_FLAG} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_USE_SYSTEM_SC=ON -DT8CODE_USE_SYSTEM_P4EST=ON -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSC_DIR=$SC_PATH/install/cmake -DP4EST_DIR=$P4EST_PATH/install/cmake" && echo CONFIG_OPTIONS="$CONFIG_OPTIONS" >> $GITHUB_ENV # cmake and test - name: Printing MPI compiler info diff --git a/.github/workflows/tests_cmake_t8code_api.yml b/.github/workflows/tests_cmake_t8code_api.yml index 445e06d331..5505d16f0f 100644 --- a/.github/workflows/tests_cmake_t8code_api.yml +++ b/.github/workflows/tests_cmake_t8code_api.yml @@ -39,10 +39,10 @@ on: required: true type: string description: 'Build type (Release/Debug)' - LESS_TESTS: + TEST_LEVEL: required: true - type: boolean - description: 'Enable less tests option for configuring' + type: number + description: 'Test level used for configuring (0 for full tests, 1 for less thorough tests, 2 for minimal tests)' jobs: t8code_cmake_tests: @@ -80,12 +80,12 @@ jobs: # # # build config vars - - name: less-test option - if: ${{ inputs.LESS_TESTS }} - run: export LESS_TEST_OPTION="-DT8CODE_ENABLE_LESS_TESTS=ON" - && echo LESS_TEST_OPTION="$LESS_TEST_OPTION" >> $GITHUB_ENV + - name: Set test level + if: ${{ inputs.TEST_LEVEL != 0 }} + run: export TEST_LEVEL_FLAG="-DT8CODE_TEST_LEVEL=${{ inputs.TEST_LEVEL }}" + && echo TEST_LEVEL_FLAG="$TEST_LEVEL_FLAG" >> $GITHUB_ENV - name: build config variables - run: export CONFIG_OPTIONS="${LESS_TEST_OPTION} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_BUILD_FORTRAN_INTERFACE=ON -DT8CODE_USE_SYSTEM_SC=ON -DT8CODE_USE_SYSTEM_P4EST=ON -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSC_DIR=$SC_PATH/install/cmake -DP4EST_DIR=$P4EST_PATH/install/cmake" + run: export CONFIG_OPTIONS="${TEST_LEVEL_FLAG} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_BUILD_FORTRAN_INTERFACE=ON -DT8CODE_USE_SYSTEM_SC=ON -DT8CODE_USE_SYSTEM_P4EST=ON -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSC_DIR=$SC_PATH/install/cmake -DP4EST_DIR=$P4EST_PATH/install/cmake" && echo CONFIG_OPTIONS="$CONFIG_OPTIONS" >> $GITHUB_ENV # cmake and test with fortran - name: check fortran diff --git a/.github/workflows/tests_cmake_t8code_linkage.yml b/.github/workflows/tests_cmake_t8code_linkage.yml index 0785d5127d..4252710041 100644 --- a/.github/workflows/tests_cmake_t8code_linkage.yml +++ b/.github/workflows/tests_cmake_t8code_linkage.yml @@ -39,10 +39,10 @@ on: required: true type: string description: 'Build type (Release/Debug)' - LESS_TESTS: + TEST_LEVEL: required: true - type: boolean - description: 'Enable less tests option for configuring' + type: number + description: 'Test level used for configuring (0 for full tests, 1 for less thorough tests, 2 for minimal tests)' jobs: t8code_cmake_tests: @@ -80,12 +80,12 @@ jobs: # # # build config vars - - name: less-test option - if: ${{ inputs.LESS_TESTS }} - run: export LESS_TEST_OPTION="-DT8CODE_ENABLE_LESS_TESTS=ON" - && echo LESS_TEST_OPTION="$LESS_TEST_OPTION" >> $GITHUB_ENV + - name: Set test level + if: ${{ inputs.TEST_LEVEL != 0 }} + run: export TEST_LEVEL_FLAG="-DT8CODE_TEST_LEVEL=${{ inputs.TEST_LEVEL }}" + && echo TEST_LEVEL_FLAG="$TEST_LEVEL_FLAG" >> $GITHUB_ENV - name: build config variables - run: export CONFIG_OPTIONS="${LESS_TEST_OPTION} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_USE_SYSTEM_SC=ON -DT8CODE_USE_SYSTEM_P4EST=ON -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSC_DIR=$SC_PATH/install/cmake -DP4EST_DIR=$P4EST_PATH/install/cmake" + run: export CONFIG_OPTIONS="${TEST_LEVEL_FLAG} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_USE_SYSTEM_SC=ON -DT8CODE_USE_SYSTEM_P4EST=ON -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSC_DIR=$SC_PATH/install/cmake -DP4EST_DIR=$P4EST_PATH/install/cmake" && echo CONFIG_OPTIONS="$CONFIG_OPTIONS" >> $GITHUB_ENV # cmake and test with netcdf - name: check NetCDF diff --git a/.github/workflows/tests_cmake_t8code_w_shipped_submodules.yml b/.github/workflows/tests_cmake_t8code_w_shipped_submodules.yml index fbe73c3a75..d393504e92 100644 --- a/.github/workflows/tests_cmake_t8code_w_shipped_submodules.yml +++ b/.github/workflows/tests_cmake_t8code_w_shipped_submodules.yml @@ -39,10 +39,10 @@ on: required: true type: string description: 'Build type (Release/Debug)' - LESS_TESTS: + TEST_LEVEL: required: true - type: boolean - description: 'Enable less tests option for configuring' + type: number + description: 'Test level used for configuring (0 for full tests, 1 for less thorough tests, 2 for minimal tests)' jobs: t8code_cmake_tests: @@ -76,12 +76,12 @@ jobs: # T8CODE # # build config vars - - name: less-test option - if: ${{ inputs.LESS_TESTS }} - run: export LESS_TEST_OPTION="-DT8CODE_ENABLE_LESS_TESTS=ON" - && echo LESS_TEST_OPTION="$LESS_TEST_OPTION" >> $GITHUB_ENV + - name: Set test level + if: ${{ inputs.TEST_LEVEL != 0 }} + run: export TEST_LEVEL_FLAG="-DT8CODE_TEST_LEVEL=${{ inputs.TEST_LEVEL }}" + && echo TEST_LEVEL_FLAG="$TEST_LEVEL_FLAG" >> $GITHUB_ENV - name: build config variables - run: export CONFIG_OPTIONS="${LESS_TEST_OPTION} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE" + run: export CONFIG_OPTIONS="${TEST_LEVEL_FLAG} -GNinja -DCMAKE_C_FLAGS_DEBUG=${DEBUG_CONFIG} -DCMAKE_CXX_FLAGS_DEBUG=${DEBUG_CONFIG} -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_BUILD_WALL=ON -DT8CODE_BUILD_WERROR=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE" && echo CONFIG_OPTIONS="$CONFIG_OPTIONS" >> $GITHUB_ENV # cmake and test - name: Printing MPI compiler info diff --git a/.github/workflows/tests_cmake_testsuite.yml b/.github/workflows/tests_cmake_testsuite.yml index 85ef8b9462..19396d3662 100644 --- a/.github/workflows/tests_cmake_testsuite.yml +++ b/.github/workflows/tests_cmake_testsuite.yml @@ -31,15 +31,11 @@ name: t8code CMake testsuite # IGNORE_CACHE to true in the respective steps. on: + merge_group: push: branches: - main - - develop - - feature-*CI* # for testing this script, all feature branches with "CI" in their name pull_request: - branches: - - main - - develop workflow_dispatch: # Be able to trigger this manually on github.com # Run every night at 1:10 schedule: @@ -93,8 +89,7 @@ jobs: MAKEFLAGS: ${{ matrix.MAKEFLAGS }} MPI: ${{ matrix.MPI }} BUILD_TYPE: ${{ matrix.BUILD_TYPE }} - LESS_TESTS: ${{ github.event_name == 'pull_request' }} - + TEST_LEVEL: ${{ github.event_name == 'pull_request' && 1 || 0 }} # Set TEST_LEVEL to 1 if the event is a PR, otherwise 0. # Run t8code linkage tests with and without MPI and in serial and debug mode t8code_linkage_tests: @@ -112,7 +107,7 @@ jobs: MAKEFLAGS: ${{ matrix.MAKEFLAGS }} MPI: ${{ matrix.MPI }} BUILD_TYPE: ${{ matrix.BUILD_TYPE }} - LESS_TESTS: ${{ github.event_name == 'pull_request' }} + TEST_LEVEL: ${{ github.event_name == 'pull_request' && 1 || 0 }} # Set TEST_LEVEL to 1 if the event is a PR, otherwise 0. # Run t8code linkage tests with and without MPI and in serial and debug mode t8code_api_tests: @@ -130,7 +125,7 @@ jobs: MAKEFLAGS: ${{ matrix.MAKEFLAGS }} MPI: ${{ matrix.MPI }} BUILD_TYPE: ${{ matrix.BUILD_TYPE }} - LESS_TESTS: ${{ github.event_name == 'pull_request' }} + TEST_LEVEL: ${{ github.event_name == 'pull_request' && 1 || 0 }} # Set TEST_LEVEL to 1 if the event is a PR, otherwise 0. # Run t8code tests with shipped submodules. This test is only for the build system, so only one config is tested. t8code_w_shipped_submodules_tests: @@ -140,11 +135,11 @@ jobs: MAKEFLAGS: -j4 MPI: ON BUILD_TYPE: Debug - LESS_TESTS: ${{ github.event_name == 'pull_request' }} + TEST_LEVEL: ${{ github.event_name == 'pull_request' && 1 || 0 }} # Set TEST_LEVEL to 1 if the event is a PR, otherwise 0. t8code_tarball: if: (github.event_name == 'schedule' && github.repository == 'DLR-AMR/t8code') || (github.event_name != 'schedule') uses: ./.github/workflows/build_cmake_tarball.yml needs: [preparation, sc_p4est_tests, t8code_tests, t8code_linkage_tests, t8code_api_tests, t8code_w_shipped_submodules_tests] with: - LESS_TESTS: ${{ github.event_name == 'pull_request' }} + TEST_LEVEL: ${{ github.event_name == 'pull_request' && 1 || 0 }} # Set TEST_LEVEL to 1 if the event is a PR, otherwise 0. diff --git a/.github/workflows/update_documentation.yml b/.github/workflows/update_documentation.yml index c0a9b64593..ca7b5670f9 100644 --- a/.github/workflows/update_documentation.yml +++ b/.github/workflows/update_documentation.yml @@ -67,7 +67,7 @@ jobs: run: echo CFLAGS_var="-Wall -pedantic -O3" >> $GITHUB_ENV && echo CXXFLAGS_var="-Wall -pedantic -O3" >> $GITHUB_ENV - name: build config variables - run: export CONFIG_OPTIONS="--without-blas ${LESS_TEST_OPTION}" + run: export CONFIG_OPTIONS="--without-blas ${TEST_LEVEL_FLAG}" && export CONFIG_SERIAL_DEBUG="$CONFIG_OPTIONS --enable-debug --with-sc=$SC_SERIAL_DEBUG/install --with-p4est=$P4EST_SERIAL_DEBUG/install" - name: Check vars run: echo "[$CONFIG_SERIAL_DEBUG]" diff --git a/AUTHORS b/AUTHORS index ad1666da1b..bc9866178f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -26,5 +26,5 @@ Contributors: Andrew Kirby Hendrik Ranocha Michael Schlottke-Lakemper - Kathrin Schoenlein + Katrin Schoenlein diff --git a/CITATION.cff b/CITATION.cff index 31385733f9..5b38fb5fa3 100755 --- a/CITATION.cff +++ b/CITATION.cff @@ -113,9 +113,9 @@ authors: family-names: Schlottke-Lakemper email: m.schlottke-lakemper@acom.rwth-aachen.de affiliation: "RWTH Aachen" - - given-names: Kathrin + - given-names: Katrin family-names: Schoenlein - email: kathrin.schoenlein@dlr.de + email: katrin.schoenlein@dlr.de affiliation: "German Aerospace Center (DLR)" - given-names: Tabea family-names: Leistikow diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c348ad4a7..a5fa5370d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,6 @@ option( T8CODE_EXPORT_COMPILE_COMMANDS "Export the compile commands as json. Can option( T8CODE_BUILD_TESTS "Build t8code's automated tests" ON ) cmake_dependent_option( T8CODE_BUILD_TPL_TESTS "Build the tests from libsc and p4est" ON "T8CODE_BUILD_TESTS" OFF ) -cmake_dependent_option( T8CODE_ENABLE_LESS_TESTS "Tests not as thoroughly to speed up the test suite. Tests the same functionality. (WARNING: Use with care.)" OFF "T8CODE_BUILD_TESTS" OFF ) option( T8CODE_BUILD_EXAMPLES "Build t8code's examples" ON ) cmake_dependent_option( T8CODE_BUILD_TPL_EXAMPLES "Build the examples from libsc and p4est" OFF "T8CODE_BUILD_EXAMPLES" OFF ) @@ -41,6 +40,8 @@ option( T8CODE_USE_SYSTEM_P4EST "Use system-installed p4est library" OFF ) option( T8CODE_BUILD_DOCUMENTATION "Build t8code's documentation" OFF ) cmake_dependent_option( T8CODE_BUILD_DOCUMENTATION_SPHINX "Build t8code's documentation using sphinx" OFF "T8CODE_BUILD_DOCUMENTATION" OFF ) +set(T8CODE_TEST_LEVEL 0 CACHE STRING "Test level: 0 for full tests, 1 for less thorough tests, 2 for minimal tests. (WARNING: Use with care.)") +add_definitions(-DT8CODE_TEST_LEVEL=${T8CODE_TEST_LEVEL}) set(T8CODE_CUSTOM_PARALLEL_TEST_COMMAND "" CACHE STRING "Define a custom command for parallel tests , e.g.: mpirun -np 8 (overwrites standard mpirun -np 4 if build with mpi)") set(T8CODE_CUSTOM_SERIAL_TEST_COMMAND "" CACHE STRING "Define a custom command for serial tests.") diff --git a/README.md b/README.md index 5010ff1049..babcbcb9ab 100644 --- a/README.md +++ b/README.md @@ -72,15 +72,10 @@ We provide a short guide to install t8code in our Wiki [Installation guide](http ### Documentation -t8code uses [Doxygen](https://doxygen.nl/) to generate the code documentation. You can build the documentation with +t8code uses [Doxygen](https://doxygen.nl/) to generate the code documentation. +You can find the documentation of our releases on the [t8code website](https://dlr-amr.github.io/t8code/pages/documentation.html). +Follow the steps described in our Wiki [Documentation](https://github.com/DLR-AMR/t8code/wiki/Documentation) to create the documentation locally. -``` -make doxygen -``` - -and then find the generated files in the `/doc` subfolder. - -You can also find the documentation of our releases on the [t8code website](https://dlr-amr.github.io/t8code/pages/documentation.html). ### License and contributing t8code is licensed under GPLv2 (see [COPYING](COPYING)). We appreciate @@ -134,6 +129,9 @@ to install the package on your system. [6] **Geometry controlled refinement for hexahedra**: Elsweijer, Sandro and Holke, Johannes and Kleinert, Jan and Reith, Dirk (2022) *Constructing a Volume Geometry Map for Hexahedra with Curved Boundary Geometries*. In: SIAM International Meshing Roundtable Workshop 2022. SIAM International Meshing Roundtable Workshop 2022, 22. - 25. Feb. 2022, [Full text available](https://elib.dlr.de/186570/1/ConstructingAVolumeGeometryMapForHexahedraWithCurvedBoundaryGeometries.pdf) + [7] **JOSS entry**: + Holke, Johannes and Markert, Johannes, et. al. (2025) *t8code - modular adaptive mesh refinement in the exascale era*. In: Journal of Open Source Software, [Full text available](https://www.theoj.org/joss-papers/joss.06887/10.21105.joss.06887.pdf) + ### Theses with t8code relations An (incomplete) list of theses written with or about t8code: diff --git a/api/t8_fortran_interface/CMakeLists.txt b/api/t8_fortran_interface/CMakeLists.txt index 3072277cd3..3636be24b6 100644 --- a/api/t8_fortran_interface/CMakeLists.txt +++ b/api/t8_fortran_interface/CMakeLists.txt @@ -8,11 +8,11 @@ target_include_directories( T8 PRIVATE ${CMAKE_CURRENT_LIST_DIR} ) # Install header files. install( FILES ${CMAKE_CURRENT_LIST_DIR}/t8_fortran_interface.h - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/t8_fortran_interface + DESTINATION include/t8_fortran_interface ) # Install module files. install( FILES ${CMAKE_BINARY_DIR}/src/t8_fortran_interface_mod.mod - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/t8_fortran_interface + DESTINATION include/t8_fortran_interface ) diff --git a/api/t8_fortran_interface/t8_fortran_interface.c b/api/t8_fortran_interface/t8_fortran_interface.c index 7606ab9e1b..4c2f6c6d8c 100644 --- a/api/t8_fortran_interface/t8_fortran_interface.c +++ b/api/t8_fortran_interface/t8_fortran_interface.c @@ -3,7 +3,7 @@ t8code is a C library to manage a collection (a forest) of multiple connected adaptive space-trees of general element classes in parallel. - Copyright (C) 2024 the developers + Copyright (C) 2025 the developers t8code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/api/t8_fortran_interface/t8_fortran_interface.h b/api/t8_fortran_interface/t8_fortran_interface.h index 195205f280..e6bdf2104d 100644 --- a/api/t8_fortran_interface/t8_fortran_interface.h +++ b/api/t8_fortran_interface/t8_fortran_interface.h @@ -3,7 +3,7 @@ t8code is a C library to manage a collection (a forest) of multiple connected adaptive space-trees of general element classes in parallel. - Copyright (C) 2024 the developers + Copyright (C) 2025 the developers t8code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 59916e57d7..aec3145190 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -25,12 +25,12 @@ function( add_t8_benchmark ) set_target_properties( ${ADD_T8_BENCHMARK_NAME} PROPERTIES EXPORT_COMPILE_COMMANDS ON ) endif( T8CODE_EXPORT_COMPILE_COMMANDS ) - install( TARGETS ${ADD_T8_BENCHMARK_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin ) + install( TARGETS ${ADD_T8_BENCHMARK_NAME} DESTINATION bin ) endfunction() -add_t8_benchmark( NAME t8_time_partition SOURCES time_partition.cxx ) -add_t8_benchmark( NAME t8_time_forest_partition SOURCES time_forest_partition.cxx ) +add_t8_benchmark( NAME t8_time_partition SOURCES t8_time_partition.cxx ) +add_t8_benchmark( NAME t8_time_forest_partition SOURCES t8_time_forest_partition.cxx ) add_t8_benchmark( NAME t8_time_prism_adapt SOURCES t8_time_prism_adapt.cxx ) add_t8_benchmark( NAME t8_time_fractal SOURCES t8_time_fractal.cxx ) add_t8_benchmark( NAME t8_time_set_join_by_vertices SOURCES t8_time_set_join_by_vertices.cxx ) -add_t8_benchmark( NAME t8_time_new_refine SOURCES time_new_refine.c ) +add_t8_benchmark( NAME t8_time_new_refine SOURCES t8_time_new_refine.c ) diff --git a/benchmarks/time_forest_partition.cxx b/benchmarks/t8_time_forest_partition.cxx similarity index 95% rename from benchmarks/time_forest_partition.cxx rename to benchmarks/t8_time_forest_partition.cxx index b38a562d6d..0244a332e8 100644 --- a/benchmarks/time_forest_partition.cxx +++ b/benchmarks/t8_time_forest_partition.cxx @@ -38,7 +38,8 @@ #include #include #include -#include +#include +#include /* This is the user defined data used to define the * region in which we partition. @@ -48,29 +49,11 @@ typedef struct { double c_min, c_max; /* constants that define the thickness of the refinement region */ - double normal[3]; /* normal vector to the plane E */ + t8_3D_vec normal; /* normal vector to the plane E */ int base_level; /* A given level that is not coarsend further, see -l argument */ int max_level; /* A max level that is not refined further, see -L argument */ } adapt_data_t; -/* Simple 3 dimensional vector product */ -static double -t8_vec3_dot (double *v1, double *v2) -{ - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; -} - -/* Set x = x - alpha*y - * for 2 3dim vectors x,y and a constant alpha */ -static void -t8_vec3_xmay (double *x, double alpha, double *y) -{ - int i; - for (i = 0; i < 3; i++) { - x[i] -= alpha * y[i]; - } -} - #if 0 /* TODO: deprecated. was replaced by t8_common_midpoint. */ static void @@ -106,33 +89,32 @@ t8_band_adapt (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tr t8_element_t *elements[]) { int level, base_level, max_level; - double elem_midpoint[3]; - double *normal; + t8_3D_vec elem_midpoint; adapt_data_t *adapt_data; T8_ASSERT (!is_family || num_elements == scheme->element_get_num_children (tree_class, elements[0])); level = scheme->element_get_level (tree_class, elements[0]); /* Get the minimum and maximum x-coordinate from the user data pointer of forest */ adapt_data = (adapt_data_t *) t8_forest_get_user_data (forest); - normal = adapt_data->normal; + t8_3D_vec normal = adapt_data->normal; base_level = adapt_data->base_level; max_level = adapt_data->max_level; /* Compute the coordinates of the anchor node. */ - t8_forest_element_centroid (forest_from, which_tree, elements[0], elem_midpoint); + t8_forest_element_centroid (forest_from, which_tree, elements[0], elem_midpoint.data ()); /* Calculate elem_midpoint - c_min n */ - t8_vec3_xmay (elem_midpoint, adapt_data->c_min, normal); + t8_axy (elem_midpoint, normal, adapt_data->c_min); /* The purpose of the factor C*h is that the levels get smaller, the * closer we get to the interface. We refine a cell if it is at most * C times its own height away from the interface */ - if (t8_vec3_dot (elem_midpoint, normal) >= 0) { + if (t8_dot (elem_midpoint, normal) >= 0) { /* if the anchor node is to the right of c_min*E, * check if it is to the left of c_max*E */ /* set elem_midpoint to the original anchor - c_max*normal */ - t8_vec3_xmay (elem_midpoint, adapt_data->c_max - adapt_data->c_min, normal); - if (t8_vec3_dot (elem_midpoint, normal) <= 0) { + t8_axy (elem_midpoint, normal, adapt_data->c_max - adapt_data->c_min); + if (t8_dot (elem_midpoint, normal) <= 0) { if (level < max_level) { /* We do refine if level smaller 1+base level and the anchor is * to the left of c_max*E */ @@ -154,16 +136,6 @@ t8_band_adapt (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tr return 0; } -static void -t8_vec3_normalize (double *v) -{ - double norm = sqrt (t8_vec3_dot (v, v)); - - v[0] /= norm; - v[1] /= norm; - v[2] /= norm; -} - /* Create a cmesh from a .msh files uniform level 0 * partitioned. */ static void @@ -212,7 +184,7 @@ t8_time_forest_cmesh_mshfile (t8_cmesh_t cmesh, const char *vtu_prefix, sc_MPI_C adapt_data.normal[0] = 0.8; adapt_data.normal[1] = 0.3; adapt_data.normal[2] = 0.0; - t8_vec3_normalize (adapt_data.normal); + t8_normalize (adapt_data.normal); adapt_data.base_level = init_level; adapt_data.max_level = max_level; /* Start the time loop, in each time step the refinement front moves diff --git a/benchmarks/time_new_refine.c b/benchmarks/t8_time_new_refine.c similarity index 100% rename from benchmarks/time_new_refine.c rename to benchmarks/t8_time_new_refine.c diff --git a/benchmarks/time_partition.cxx b/benchmarks/t8_time_partition.cxx similarity index 100% rename from benchmarks/time_partition.cxx rename to benchmarks/t8_time_partition.cxx diff --git a/benchmarks/time_refine_type03.c b/benchmarks/time_refine_type03.c deleted file mode 100644 index b6c9ba3874..0000000000 --- a/benchmarks/time_refine_type03.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - This file is part of t8code. - t8code is a C library to manage a collection (a forest) of multiple - connected adaptive space-trees of general element types in parallel. - - Copyright (C) 2015 the developers - - t8code 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. - - t8code 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 t8code; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* TODO: This file should not be included from an application */ - -int max_ref_level = 0; - -/* This function refines every element */ -static int -t8_basic_adapt_refine_type (t8_forest_t forest, t8_locidx_t which_tree, t8_eclass_t tree_class, - const t8_scheme_c *scheme, const int is_family, int num_elements, t8_element_t *elements[]) -{ - T8_ASSERT (!is_family || num_elements == t8_eclass_num_children[tree_class]); - - const int dim = t8_eclass_to_dimension[tree_class]; - const int level = t8_element_get_level (forest, tree_class, elements[0]); - if (level >= max_ref_level) { - return 0; - } - /* get the type of the current element */ - const int type = dim == 2 ? ((t8_dtri_t *) elements[0])->type : ((t8_dtet_t *) elements[0])->type; - /* refine type 0 and 3 */ - if (type == 0 || type == 3) { - return 1; - } - return 0; -} - -static void -t8_timings_adapt_type (int start_l, int dim) -{ - t8_forest_t forests[2]; - t8_eclass_t eclass; - sc_flopinfo_t fi, snapshot; - sc_statinfo_t stats[1]; - long long num_el; - - t8_forest_init (&forests[0]); - - eclass = dim == 2 ? T8_ECLASS_TRIANGLE : T8_ECLASS_TET; - - t8_forest_set_cmesh (forests[0], t8_cmesh_new_bigmesh (eclass, 512, sc_MPI_COMM_WORLD, 0), sc_MPI_COMM_WORLD); - t8_forest_set_scheme (forests[0], t8_scheme_new_default ()); - t8_forest_set_level (forests[0], start_l); - t8_forest_commit (forests[0]); - - t8_forest_init (&forests[1]); - t8_forest_set_adapt (forests[1], forests[0], t8_basic_adapt_refine_type, 1); - - sc_flops_start (&fi); - sc_flops_snap (&fi, &snapshot); - - t8_forest_commit (forests[1]); - - sc_flops_shot (&fi, &snapshot); - sc_stats_set1 (&stats[0], snapshot.iwtime, "New"); - - num_el = (long long) forests[1]->local_num_elements; - - t8_forest_unref (&forests[1]); - - t8_debugf ("=P= I have %lli elements\n", num_el); - sc_stats_compute (sc_MPI_COMM_WORLD, 1, stats); - sc_stats_print (t8_get_package_id (), SC_LP_STATISTICS, 1, stats, 1, 1); -} - -int -main (int argc, char **argv) -{ - int mpiret, mpisize; - int start_level, end_level, dim; - int first_argc; - sc_options_t *opt; - - mpiret = sc_MPI_Init (&argc, &argv); - SC_CHECK_MPI (mpiret); - - sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); - p4est_init (NULL, SC_LP_ESSENTIAL); - t8_init (SC_LP_DEFAULT); - - mpiret = sc_MPI_Comm_size (sc_MPI_COMM_WORLD, &mpisize); - SC_CHECK_MPI (mpiret); - - opt = sc_options_new (argv[0]); - sc_options_add_int (opt, 's', "slevel", &start_level, 0, "initial refine level"); - sc_options_add_int (opt, 'e', "elevel", &end_level, 0, - "Final refine level: greater or equal to initial refine level"); - sc_options_add_int (opt, 'd', "dim", &dim, 2, "dimension: 2 or 3"); - first_argc = sc_options_parse (t8_get_package_id (), SC_LP_DEFAULT, opt, argc, argv); - if (first_argc < 0 || first_argc != argc || 2 > dim || dim > 3 || end_level < start_level) { - sc_options_print_usage (t8_get_package_id (), SC_LP_ERROR, opt, NULL); - return 1; - } - - max_ref_level = end_level; - t8_timings_adapt_type (start_level, dim); - - sc_options_destroy (opt); - - sc_finalize (); - - mpiret = sc_MPI_Finalize (); - SC_CHECK_MPI (mpiret); - - return 0; -} diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 541155e66a..840c5cba26 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -457,7 +457,7 @@ LOOKUP_CACHE_SIZE = 0 # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you +# which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. diff --git a/doc/author_brandt.txt b/doc/author_brandt.txt new file mode 100644 index 0000000000..095dab52be --- /dev/null +++ b/doc/author_brandt.txt @@ -0,0 +1 @@ +I place my contributions to t8code under the FreeBSD license. Hannes Brandt (brandt@ins.uni-bonn.de) diff --git a/doc/author_nguyenxuan.txt b/doc/author_nguyenxuan.txt new file mode 100644 index 0000000000..cb949c35c7 --- /dev/null +++ b/doc/author_nguyenxuan.txt @@ -0,0 +1,2 @@ +I place my contributions to t8code under the FreeBSD license. +Tu Nguyen Xuan (tu.nguyenxuan@dlr.de) \ No newline at end of file diff --git a/doc/author_ploetzke.txt b/doc/author_ploetzke.txt new file mode 100644 index 0000000000..f33e3e26b3 --- /dev/null +++ b/doc/author_ploetzke.txt @@ -0,0 +1 @@ +I place my contributions to t8code under the FreeBSD license. Lena Plötzke (lena.ploetzke@gmx.de) \ No newline at end of file diff --git a/doc/author_spenke.txt b/doc/author_spenke.txt new file mode 100644 index 0000000000..0e7b6ec0dc --- /dev/null +++ b/doc/author_spenke.txt @@ -0,0 +1 @@ +I place my contributions to t8code under the FreeBSD license. Thomas Spenke (thomas.spenke@dlr.de) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index a01a9c6e6e..33393167f9 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -7,7 +7,7 @@ endif() target_sources( t8example PRIVATE common/t8_example_common.cxx common/t8_example_common_functions.cxx ) target_include_directories( t8example PRIVATE ${CMAKE_CURRENT_LIST_DIR}/.. ${SC_INCLUDE_DIR} ) target_link_libraries( t8example PRIVATE T8 ${SC_LIBRARIES} m ) -install( TARGETS t8example DESTINATION ${CMAKE_INSTALL_PREFIX}/lib ) +install( TARGETS t8example DESTINATION lib ) function( add_t8_example ) set( options "" ) @@ -36,7 +36,7 @@ function( add_t8_example ) set_target_properties( ${ADD_T8_EXAMPLE_NAME} PROPERTIES EXPORT_COMPILE_COMMANDS ON ) endif( T8CODE_EXPORT_COMPILE_COMMANDS ) - install( TARGETS ${ADD_T8_EXAMPLE_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin ) + install( TARGETS ${ADD_T8_EXAMPLE_NAME} DESTINATION bin ) endfunction() add_t8_example( NAME t8_advection SOURCES advect/t8_advection.cxx ) diff --git a/example/IO/cmesh/gmsh/t8_load_and_refine_square_w_hole.cxx b/example/IO/cmesh/gmsh/t8_load_and_refine_square_w_hole.cxx index 907c3b3174..992da19ee4 100644 --- a/example/IO/cmesh/gmsh/t8_load_and_refine_square_w_hole.cxx +++ b/example/IO/cmesh/gmsh/t8_load_and_refine_square_w_hole.cxx @@ -33,24 +33,7 @@ #include #include #include - -/* Simple 3 dimensional vector product */ -static double -t8_vec3_dot (double *v1, double *v2) -{ - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; -} - -/* Set x = x - alpha*y - * for 2 3dim vectors x,y and a constant alpha */ -static void -t8_vec3_xmay (double *x, double alpha, double *y) -{ - int i; - for (i = 0; i < 3; i++) { - x[i] -= alpha * y[i]; - } -} +#include /* Compute the coordinates of the midpoint * and a measure for the length of a triangle or square */ @@ -86,7 +69,7 @@ t8_midpoint (t8_forest_t forest, t8_locidx_t which_tree, t8_element_t *element, for (j = 0; j < 3; j++) { corner[0][j] -= elem_mid_point[j]; } - *h = sqrt (t8_vec3_dot (corner[0], corner[0])); + *h = t8_norm (corner[0]); T8_FREE (corner[0]); T8_FREE (corner[1]); @@ -105,10 +88,10 @@ t8_midpoint (t8_forest_t forest, t8_locidx_t which_tree, t8_element_t *element, /* Now that we now the midpoint, we can compute h */ for (i = 0; i < 3; i++) { /* Compute the difference of the mid vertex and the i-th vertex */ - t8_vec3_xmay (corner[i], 1, elem_mid_point); + t8_axy (corner[i], elem_mid_point, 1); /* Set the size of the element to the euclidean distance of the two * vertices if it is bigger than the previous distance */ - *h = SC_MAX (sqrt (t8_vec3_dot (corner[i], corner[i])), *h); + *h = SC_MAX (t8_norm (corner[i]), *h); T8_FREE (corner[i]); } } @@ -137,7 +120,7 @@ t8_load_refine_adapt (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t w } /* Refine along the inner boundary. * The factor in front of h controls the width of the refinement region. */ - if (tree_class == T8_ECLASS_TRIANGLE && t8_vec3_dot (elem_midpoint, elem_midpoint) < 1 + 5 * h) { + if (tree_class == T8_ECLASS_TRIANGLE && t8_dot (elem_midpoint, elem_midpoint) < 1 + 5 * h) { return 1; } diff --git a/example/IO/forest/netcdf/t8_write_forest_netcdf.cxx b/example/IO/forest/netcdf/t8_write_forest_netcdf.cxx index 4e2a485661..4be56a5185 100644 --- a/example/IO/forest/netcdf/t8_write_forest_netcdf.cxx +++ b/example/IO/forest/netcdf/t8_write_forest_netcdf.cxx @@ -36,7 +36,7 @@ #define NC_COLLECTIVE 1 #endif #include -#include +#include #include #include #include @@ -60,7 +60,7 @@ T8_EXTERN_C_BEGIN (); */ struct t8_example_netcdf_adapt_data { - double midpoint[3]; /* Midpoint of a aphere */ + t8_3D_point midpoint; /* Midpoint of a aphere */ double refine_if_inside_radius; /* refine all elements inside this radius from the sphere's midpoint */ double coarsen_if_outside_radius; /* coarsen all element families outside of this radius from the sphere's midpoint */ }; @@ -74,7 +74,7 @@ t8_example_netcdf_adapt_fn (t8_forest_t forest, t8_forest_t forest_from, t8_loci const t8_eclass_t tree_class, t8_locidx_t lelement_id, const t8_scheme *scheme, const int is_family, const int num_elements, t8_element_t *elements[]) { - double element_centroid[3]; + t8_3D_point element_centroid; double distance; /* Retrieve the adapt_data which holds the information regarding the adaption process of a forest */ @@ -82,10 +82,10 @@ t8_example_netcdf_adapt_fn (t8_forest_t forest, t8_forest_t forest_from, t8_loci = (const struct t8_example_netcdf_adapt_data *) t8_forest_get_user_data (forest); /* Compute the element's centroid */ - t8_forest_element_centroid (forest_from, which_tree, elements[0], element_centroid); + t8_forest_element_centroid (forest_from, which_tree, elements[0], element_centroid.data ()); /* Compute the distance from the element's midpoint to the midpoint of the centered sphere inside the hypercube */ - distance = t8_vec_dist (element_centroid, adapt_data->midpoint); + distance = t8_dist (element_centroid, adapt_data->midpoint); /* Decide whether the element (or its family) has to be refined or coarsened */ if (distance < adapt_data->refine_if_inside_radius) { @@ -116,9 +116,9 @@ t8_example_netcdf_adapt (t8_forest_t forest) /* The adapt data which controls which elements will be refined or corsened based on the given radii */ struct t8_example_netcdf_adapt_data adapt_data = { - { 0.5, 0.5, 0.5 }, /* Midpoints of the sphere. */ - 0.2, /* Refine if inside this radius. */ - 0.4 /* Coarsen if outside this radius. */ + t8_3D_point ({ 0.5, 0.5, 0.5 }), /* Midpoints of the sphere. */ + 0.2, /* Refine if inside this radius. */ + 0.4 /* Coarsen if outside this radius. */ }; /* Create the adapted forest with the given adapt_function. */ diff --git a/example/advect/t8_advection.cxx b/example/advect/t8_advection.cxx index fb75a67204..0f32778448 100644 --- a/example/advect/t8_advection.cxx +++ b/example/advect/t8_advection.cxx @@ -30,12 +30,12 @@ #include #include #include -#include +#include #include #include #include #include -#include +#include #define MAX_FACES 8 /* The maximum number of faces of an element */ /* TODO: This is not memory efficient. If we run out of memory, we can optimize here. */ @@ -139,7 +139,7 @@ typedef struct /** The per element data */ typedef struct { - double midpoint[3]; /**< coordinates of element midpoint in R^3 */ + t8_3D_point midpoint; /**< coordinates of element midpoint in R^3 */ double vol; /**< Volume of this element */ double phi_new; /**< Value of solution at midpoint in next time step */ double *fluxes[MAX_FACES]; /**< The fluxes to each neeighbor at a given face */ @@ -324,9 +324,9 @@ static double t8_advect_flux_upwind_1d (const t8_advect_problem_t *problem, const t8_locidx_t el_plus, const t8_locidx_t el_minus, int face) { - double x_j_half[3]; + t8_3D_point x_j_half; int idim; - double u_at_x_j_half[3]; + t8_3D_vec u_at_x_j_half; double phi; int sign; t8_advect_element_data_t *el_data_plus; @@ -366,10 +366,10 @@ static double t8_advect_flux_upwind (const t8_advect_problem_t *problem, double el_plus_phi, double el_minus_phi, t8_locidx_t ltreeid, const t8_element_t *element_plus, int face) { - double face_center[3]; - double u_at_face_center[3]; - double normal[3], normal_times_u; - double area; + t8_3D_point face_center; + t8_3D_vec u_at_face_center; + t8_3D_vec normal; + double area, normal_times_u; /* * | --x-- | --x-- | Two elements, midpoints marked with 'x' @@ -378,16 +378,16 @@ t8_advect_flux_upwind (const t8_advect_problem_t *problem, double el_plus_phi, d */ /* Compute the center coordinate of the face */ - t8_forest_element_face_centroid (problem->forest, ltreeid, element_plus, face, face_center); + t8_forest_element_face_centroid (problem->forest, ltreeid, element_plus, face, face_center.data ()); /* Compute u at the face center. */ problem->u (face_center, problem->t, u_at_face_center); /* Compute the normal of the element at this face */ - t8_forest_element_face_normal (problem->forest, ltreeid, element_plus, face, normal); + t8_forest_element_face_normal (problem->forest, ltreeid, element_plus, face, normal.data ()); /* Compute the area of the face */ area = t8_forest_element_face_area (problem->forest, ltreeid, element_plus, face); /* Compute the dot-product of u and the normal vector */ - normal_times_u = t8_vec_dot (normal, u_at_face_center); + normal_times_u = t8_dot (normal, u_at_face_center); if (normal_times_u >= 0) { return -el_plus_phi * normal_times_u * area; @@ -519,7 +519,7 @@ t8_advect_compute_element_data (t8_advect_problem_t *problem, t8_advect_element_ const t8_element_t *element, const t8_locidx_t ltreeid) { /* Compute the midpoint coordinates of element */ - t8_forest_element_centroid (problem->forest, ltreeid, element, elem_data->midpoint); + t8_forest_element_centroid (problem->forest, ltreeid, element, elem_data->midpoint.data ()); /* Compute the length of this element */ elem_data->vol = t8_forest_element_volume (problem->forest, ltreeid, element); } @@ -989,7 +989,7 @@ t8_advect_problem_init_elements (t8_advect_problem_t *problem) const t8_scheme *scheme = t8_forest_get_scheme (problem->forest); t8_eclass_t neigh_eclass; double speed, max_speed = 0, min_diam = -1, delta_t, min_delta_t; - double u[3]; + t8_3D_vec u; double diam; double min_vol = 1e9; @@ -1010,7 +1010,7 @@ t8_advect_problem_init_elements (t8_advect_problem_t *problem) min_diam = min_diam < 0 ? diam : SC_MIN (min_diam, diam); /* Compute the maximum velocity */ problem->u (elem_data->midpoint, problem->t, u); - speed = t8_vec_norm (u); + speed = t8_norm (u); max_speed = SC_MAX (max_speed, speed); /* Compute minimum necessary time step */ @@ -1068,7 +1068,8 @@ t8_advect_problem_init_elements (t8_advect_problem_t *problem) static void t8_advect_write_vtk (t8_advect_problem_t *problem) { - double *u_and_phi_array[4], u_temp[3]; + double *u_and_phi_array[4]; + t8_3D_vec u_temp; t8_locidx_t num_local_elements, ielem; t8_vtk_data_field_t vtk_data[5]; t8_advect_element_data_t *elem_data; diff --git a/example/common/t8_example_common.cxx b/example/common/t8_example_common.cxx index 5a96b58411..0a3ceb5eb1 100644 --- a/example/common/t8_example_common.cxx +++ b/example/common/t8_example_common.cxx @@ -28,9 +28,7 @@ #include #include #include -#include - -T8_EXTERN_C_BEGIN (); +#include /* Adapt a forest such that always the second child of the first * tree is refined and no other elements. This results in a highly @@ -66,7 +64,8 @@ int t8_common_within_levelset (t8_forest_t forest, const t8_locidx_t ltreeid, const t8_element_t *element, t8_example_level_set_fn levelset, double band_width, double t, void *udata) { - double elem_midpoint[3], elem_diam; + t8_3D_point elem_midpoint; + double elem_diam; double value; const t8_eclass_t tree_class = t8_forest_get_eclass (forest, ltreeid); const t8_scheme *scheme = t8_forest_get_scheme (forest); @@ -76,17 +75,17 @@ t8_common_within_levelset (t8_forest_t forest, const t8_locidx_t ltreeid, const /* If bandwidth = 0, we only refine the elements that are intersected by the zero level-set */ const int num_corners = scheme->element_get_num_corners (tree_class, element); int sign = 1, icorner; - double coords[3]; + t8_3D_point coords; /* Compute LS function at first corner */ - t8_forest_element_coordinate (forest, ltreeid, element, 0, coords); + t8_forest_element_coordinate (forest, ltreeid, element, 0, coords.data ()); /* compute the level-set function at this corner */ value = levelset (coords, t, udata); /* sign = 1 if value > 0, -1 if value < 0, 0 if value = 0 */ sign = value > 0 ? 1 : -(value < 0); /* iterate over all corners */ for (icorner = 1; icorner < num_corners; icorner++) { - t8_forest_element_coordinate (forest, ltreeid, element, icorner, coords); + t8_forest_element_coordinate (forest, ltreeid, element, icorner, coords.data ()); /* compute the level-set function at this corner */ value = levelset (coords, t, udata); if ((value > 0 && sign <= 0) || (value == 0 && sign != 0) || (value < 0 && sign >= 0)) { @@ -98,7 +97,7 @@ t8_common_within_levelset (t8_forest_t forest, const t8_locidx_t ltreeid, const } /* Compute the coordinates of the anchor node X. */ - t8_forest_element_centroid (forest, ltreeid, element, elem_midpoint); + t8_forest_element_centroid (forest, ltreeid, element, elem_midpoint.data ()); /* Compute the element's diameter */ elem_diam = t8_forest_element_diam (forest, ltreeid, element); /* Compute L(X) */ @@ -155,5 +154,3 @@ t8_common_adapt_level_set (t8_forest_t forest, t8_forest_t forest_from, t8_locid } return 0; } - -T8_EXTERN_C_END (); diff --git a/example/common/t8_example_common.h b/example/common/t8_example_common.hxx similarity index 81% rename from example/common/t8_example_common.h rename to example/common/t8_example_common.hxx index d735ca588e..38f4e27f1c 100644 --- a/example/common/t8_example_common.h +++ b/example/common/t8_example_common.hxx @@ -20,7 +20,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** file t8_example_common.h +/** file t8_example_common.hxx * This header declares datatypes and functions that are used by multiple * examples of t8code. This includes adaptation function to adapt at a zero * level-set of a level-set function and various 2 and 3 dimensional vector @@ -32,9 +32,10 @@ #include #include +#include /** A levelset function in 3+1 space dimensions. */ -typedef double (*t8_example_level_set_fn) (const double[3], double, void *); +typedef double (*t8_example_level_set_fn) (const t8_3D_point &, double, void *); /** Struct to handle refinement around a level-set function. */ typedef struct @@ -50,12 +51,10 @@ typedef struct /** Function pointer for real valued functions from d+1 space dimensions * functions f: R^d x R -> R */ typedef double (*t8_scalar_function_1d_fn) (double x, double t); -typedef double (*t8_scalar_function_2d_fn) (const double x[2], double t); -typedef double (*t8_scalar_function_3d_fn) (const double x[3], double t); +typedef double (*t8_scalar_function_2d_fn) (const t8_point<2> &x, const double t); +typedef double (*t8_scalar_function_3d_fn) (const t8_3D_point &x, const double t); /** Function pointer for a vector valued function f: R^3 x R -> R */ -typedef void (*t8_flow_function_3d_fn) (const double x_in[3], double t, double x_out[3]); - -T8_EXTERN_C_BEGIN (); +typedef void (*t8_flow_function_3d_fn) (const t8_3D_point &x_in, const double t, t8_3D_vec &x_out); /* function declarations */ @@ -104,7 +103,7 @@ t8_common_adapt_level_set (t8_forest_t forest, t8_forest_t forest_from, t8_locid typedef struct { - double M[3]; + t8_3D_point M; /**< midpoint */ double radius; /**< radius */ @@ -115,7 +114,7 @@ typedef struct * \return dist (x,data->M) - data->radius */ double -t8_levelset_sphere (const double x[3], double t, void *data); +t8_levelset_sphere (const t8_3D_point &x, double t, void *data); /** Returns always 1. * \return 1 @@ -177,68 +176,66 @@ t8_scalar3d_sint (const double x[3], double t); * \return |x| - 0.75 */ double -t8_scalar3d_sphere_75_radius (const double x[3], double t); +t8_scalar3d_sphere_75_radius (const t8_3D_vec &x, const double t); /** Level-set function of a sphere around M = (0.5,0.5,0.5) with radius 0.375 * \return |x - M| - 0.375 */ double -t8_scalar3d_sphere_05_midpoint_375_radius (const double x[3], double t); +t8_scalar3d_sphere_05_midpoint_375_radius (const t8_3D_vec &x, const double t); /** Level-set function of a sphere around M = (0.3,0.3,0.3) with radius 0.25 * \return |x - M| - 0.25 */ double -t8_scalar3d_sphere_03_midpoint_25_radius (const double x[3], double t); +t8_scalar3d_sphere_03_midpoint_25_radius (const t8_3D_vec &x, const double t); /** Level-set function of a sphere around M = (0.5,0.5,0) with radius 0.375 * \return |x - M| - 0.375 */ double -t8_scalar3d_sphere_05_0z_midpoint_375_radius (const double x[3], double t); +t8_scalar3d_sphere_05_0z_midpoint_375_radius (const t8_3D_vec &x, const double t); /** Flow functions */ /** Returns always 1 in each coordinate. */ void -t8_flow_constant_one_vec (const double x[3], double t, double x_out[3]); +t8_flow_constant_one_vec (const t8_3D_point &x, const double t, t8_3D_vec &x_out); /** Sets the first coordinate to 1, all other to 0. */ void -t8_flow_constant_one_x_vec (const double x[3], double t, double x_out[3]); +t8_flow_constant_one_x_vec (const t8_3D_point &x, const double t, t8_3D_vec &x_ou); /** Sets the first and second coordinate to 1, the third to 0. */ void -t8_flow_constant_one_xy_vec (const double x[3], double t, double x_out[3]); +t8_flow_constant_one_xy_vec (const t8_3D_point &x, const double t, t8_3D_vec &x_out); /** Sets all coordinates to a nonzero constant. */ void -t8_flow_constant_one_xyz_vec (const double x[3], double t, double x_out[3]); +t8_flow_constant_one_xyz_vec (const t8_3D_point &x, const double t, t8_3D_vec &x_out); /** Transform the unit square to [-0.5,0.5]^2 and computes * x = 2pi*y, y = -2pi*x */ void -t8_flow_rotation_2d (const double x[3], double t, double x_out[3]); +t8_flow_rotation_2d (const t8_3D_point &x, const double t, t8_3D_vec &x_out); void -t8_flow_compressible (const double x_in[3], double t, double x_out[3]); +t8_flow_compressible (const t8_3D_point &x_in, const double t, t8_3D_vec &x_out); /** Incompressible flow in unit cube */ void -t8_flow_incomp_cube_flow (const double x[3], double t, double x_out[3]); +t8_flow_incomp_cube_flow (const t8_3D_point &x, const double t, t8_3D_vec &x_out); /** 2d flow around a circle with radius R = 1 and * constant inflow with x-speed U = 1. * See https://doi.org/10.13140/RG.2.2.34714.11203 */ void -t8_flow_around_circle (const double x[3], double t, double x_out[3]); +t8_flow_around_circle (const t8_3D_point &x, const double t, t8_3D_vec &x_out); void -t8_flow_stokes_flow_sphere_shell (const double x[3], double t, double x_out[3]); +t8_flow_stokes_flow_sphere_shell (const t8_3D_point &x_in, const double t, t8_3D_vec &x_out); void -t8_flow_around_circle_with_angular_velocity (const double x[3], double t, double x_out[]); - -T8_EXTERN_C_END (); +t8_flow_around_circle_with_angular_velocity (const t8_3D_point &x, const double t, t8_3D_vec &x_out); #endif /* !T8_EXAMPLE_COMMON_H */ diff --git a/example/common/t8_example_common_functions.cxx b/example/common/t8_example_common_functions.cxx index a205fc9aee..da13097bdc 100644 --- a/example/common/t8_example_common_functions.cxx +++ b/example/common/t8_example_common_functions.cxx @@ -23,18 +23,16 @@ /** \file t8_example_common_functions.cxx Provide real valued functions * that are used in more than one example. */ -#include -#include - -T8_EXTERN_C_BEGIN (); +#include +#include double -t8_levelset_sphere (const double x[3], double t, void *data) +t8_levelset_sphere (const t8_3D_point &x, const double t, void *data) { t8_levelset_sphere_data_t *ls_data = (t8_levelset_sphere_data_t *) data; T8_ASSERT (ls_data->radius > 0); - return t8_vec_dist (x, ls_data->M) - ls_data->radius; + return t8_dist (x, ls_data->M) - ls_data->radius; } double @@ -120,63 +118,63 @@ t8_scalar3d_sint (const double x[3], double t) /* general level set function for a sphere with given midpoint and radius. */ static double -t8_scalar3d_sphere (const double x[3], double M[3], double radius) +t8_scalar3d_sphere (const t8_3D_vec &x, t8_3D_vec &M, const double radius) { /* Compute M - x */ - t8_vec_axpy (x, M, -1); + t8_axpy (x, M, -1); /* return |M-x| - radius */ - return t8_vec_norm (M) - radius; + return t8_norm (M) - radius; } double -t8_scalar3d_sphere_75_radius (const double x[3], double t) +t8_scalar3d_sphere_75_radius (const t8_3D_vec x, const double t) { - double M[3] = { 0, 0, 0 }; + t8_3D_vec M ({ 0, 0, 0 }); return t8_scalar3d_sphere (x, M, 0.75); } double -t8_scalar3d_sphere_05_midpoint_375_radius (const double x[3], double t) +t8_scalar3d_sphere_05_midpoint_375_radius (const t8_3D_vec x, double t) { - double M[3] = { 0.5, 0.5, 0.5 }; + t8_3D_vec M ({ 0.5, 0.5, 0.5 }); return t8_scalar3d_sphere (x, M, 0.375); } double -t8_scalar3d_sphere_03_midpoint_25_radius (const double x[3], double t) +t8_scalar3d_sphere_03_midpoint_25_radius (const t8_3D_vec &x, const double t) { - double M[3] = { 0.3, 0.3, 0.3 }; + t8_3D_vec M ({ 0.3, 0.3, 0.3 }); return t8_scalar3d_sphere (x, M, 0.25); } double -t8_scalar3d_sphere_05_0z_midpoint_375_radius (const double x[3], double t) +t8_scalar3d_sphere_05_0z_midpoint_375_radius (const t8_3D_vec &x, const double t) { - double M[3] = { 0.5, 0.5, 0 }; + t8_3D_vec M ({ 0.5, 0.5, 0 }); return t8_scalar3d_sphere (x, M, 0.375); } void -t8_flow_constant_one_vec (const double x[3], double t, double x_out[3]) +t8_flow_constant_one_vec (const t8_3D_point &x, const double t, t8_3D_vec &x_out) { x_out[0] = x_out[1] = x_out[2] = 1; } void -t8_flow_constant_one_x_vec (const double x[3], double t, double x_out[3]) +t8_flow_constant_one_x_vec (const t8_3D_point &x, const double t, t8_3D_vec &x_out) { x_out[0] = 1; x_out[1] = x_out[2] = 0; } void -t8_flow_constant_one_xy_vec (const double x[3], double t, double x_out[3]) +t8_flow_constant_one_xy_vec (const t8_3D_point &x, const double t, t8_3D_vec &x_out) { x_out[0] = 1; x_out[1] = 0.8; @@ -184,7 +182,7 @@ t8_flow_constant_one_xy_vec (const double x[3], double t, double x_out[3]) } void -t8_flow_constant_one_xyz_vec (const double x[3], double t, double x_out[3]) +t8_flow_constant_one_xyz_vec (const t8_3D_point &x, const double t, t8_3D_vec &x_out) { x_out[0] = 1; x_out[1] = 0.8; @@ -192,7 +190,7 @@ t8_flow_constant_one_xyz_vec (const double x[3], double t, double x_out[3]) } void -t8_flow_rotation_2d (const double x_in[3], double t, double x_out[3]) +t8_flow_rotation_2d (const t8_3D_point &x_in, const double t, t8_3D_vec &x_out) { double x = x_in[0], y = x_in[1]; @@ -203,11 +201,11 @@ t8_flow_rotation_2d (const double x_in[3], double t, double x_out[3]) x_out[1] = -x; x_out[2] = 0; - t8_vec_ax (x_out, 2 * M_PI); + t8_ax (x_out, 2 * M_PI); } void -t8_flow_compressible (const double x_in[3], double t, double x_out[3]) +t8_flow_compressible (const t8_3D_point &x_in, const double t, t8_3D_vec &x_out) { x_out[0] = (1. / 2 - x_in[0]); x_out[1] = 0; @@ -231,7 +229,7 @@ t8_incomp_cube_df_sin (double x) } void -t8_flow_incomp_cube_flow (const double x[3], double t, double x_out[3]) +t8_flow_incomp_cube_flow (const t8_3D_point &x, const double t, t8_3D_vec &x_out) { double (*f) (double) = t8_incomp_cube_f_sin; double (*df) (double) = t8_incomp_cube_df_sin; @@ -240,10 +238,10 @@ t8_flow_incomp_cube_flow (const double x[3], double t, double x_out[3]) x_out[1] = -1. * f (x[1]) * df (x[0]); x_out[2] = f (x[2]) * df (x[0]); - t8_vec_ax (x_out, 1. / 2); + t8_ax (x_out, 1. / 2); /* We reverse the flow at time 0.5 */ if (t > 0.5) { - t8_vec_ax (x_out, -1); + t8_ax (x_out, -1); } } @@ -254,7 +252,7 @@ t8_flow_incomp_cube_flow (const double x[3], double t, double x_out[3]) * On output: polar[0] = r, polar[1] = phi */ static void -t8_flow_2d_polar_coords (const double x[3], double polar[2]) +t8_flow_2d_polar_coords (const t8_vec<2> &x, t8_vec<2> &polar) { polar[0] = sqrt (SC_SQR (x[0]) + SC_SQR (x[1])); polar[1] = atan2 (x[1], x[0]); @@ -269,7 +267,7 @@ t8_flow_2d_polar_coords (const double x[3], double polar[2]) * */ static void -t8_flow_2d_cart_coords (const double polar_values[2], const double polar_coords[2], double cart[2]) +t8_flow_2d_cart_coords (const t8_vec<2> &polar_values, const t8_vec<2> &polar_coords, t8_vec<2> &cart) { cart[0] = cos (polar_coords[1]) * polar_values[0] - sin (polar_coords[1]) * polar_values[1]; cart[1] = sin (polar_coords[1]) * polar_values[0] + cos (polar_coords[1]) * polar_values[1]; @@ -278,27 +276,30 @@ t8_flow_2d_cart_coords (const double polar_values[2], const double polar_coords[ /* 2d flow around a circle with radius R = 1 and * constant inflow with x-speed U = 1. */ void -t8_flow_around_circle (const double x[3], double t, double x_out[3]) +t8_flow_around_circle (const t8_3D_point &x, const double t, t8_3D_vec &x_out) { - double polar[2]; - double polar_speed[2]; + t8_vec<2> polar; + t8_vec<2> polar_speed; + const t8_vec<2> x_2D = t8_vec<2> ({ x[0], x[1] }); + t8_vec<2> x_out_2D ({ x_out[0], x_out[1] }); const double R = 0.15; - t8_vec_axb (x, x_out, 1, -0.5); + t8_axb (x_2D, x_out_2D, 1, -0.5); /* Set the z-coordinate to zero */ - x_out[2] = 0; - if (t8_vec_norm (x_out) < R) { + if (t8_norm (x_out) < R) { /* Set the velocity inside the circle to 0 */ - x_out[0] = x_out[1] = x_out[2] = 0; + x_out[0] = x_out[1] = 0; return; } /* Convert x,y coordinates to polar r,phi coordinates */ - t8_flow_2d_polar_coords (x_out, polar); + t8_flow_2d_polar_coords (x_out_2D, polar); /* Compute v_r (r,phi) = U (1-R^2/r^2)cos(phi) */ polar_speed[0] = (1 - SC_SQR (R) / SC_SQR (polar[0])) * cos (polar[1]); /* Compute v_phi(r,phi) = -U (1+ R^2/r^2) sin (phi) */ polar_speed[1] = -(1 + SC_SQR (R) / SC_SQR (polar[0])) * sin (polar[1]); - t8_flow_2d_cart_coords ((const double *) polar_speed, polar, x_out); + t8_flow_2d_cart_coords (polar_speed, polar, x_out_2D); + x_out[0] = x_out_2D[0]; + x_out[1] = x_out_2D[1]; x_out[2] = 0; } @@ -334,7 +335,7 @@ t8_flow_stokes_sphere_f_component (double radius, double alpha, double beta, int } void -t8_flow_stokes_flow_sphere_shell (const double x[3], double t, double x_out[]) +t8_flow_stokes_flow_sphere_shell (const t8_3D_point &x_in, const double t, t8_3D_vec &x_out) { double radius; double theta, phi; @@ -343,16 +344,11 @@ t8_flow_stokes_flow_sphere_shell (const double x[3], double t, double x_out[]) double vel_theta; double vel_phi; const double r_1 = .5, r_2 = 1, gamma = 1, m = 3; - /* translate unit cube to cube centered around origin */ - ((double *) x)[0] -= 0.5; - ((double *) x)[1] -= 0.5; - ((double *) x)[2] -= 0.5; - ((double *) x)[0] *= 2; - ((double *) x)[1] *= 2; - ((double *) x)[2] *= 2; + t8_3D_vec x ({ (x_in[0] - 0.5 * 2), (x_in[1] - 0.5 * 2), (x_in[2] - 0.5 * 2) }); + /* Compute spherical coordinates */ - radius = t8_vec_norm (x); + radius = t8_norm (x); theta = acos (x[2] / radius); /* Phi component, not used */ phi = atan2 (x[1], x[0]); @@ -379,7 +375,7 @@ t8_flow_stokes_flow_sphere_shell (const double x[3], double t, double x_out[]) } void -t8_flow_around_circle_with_angular_velocity (const double x[3], double t, double x_out[]) +t8_flow_around_circle_with_angular_velocity (const t8_3D_point &x, const double t, t8_3D_vec &x_out) { const double radius = 0.5; const double omega = 1.5 * M_PI; @@ -396,5 +392,3 @@ t8_flow_around_circle_with_angular_velocity (const double x[3], double t, double x_out[1] = sin (theta) * u_r + cos (theta) * u_theta; x_out[2] = 0; } - -T8_EXTERN_C_END (); diff --git a/example/forest/t8_test_ghost.cxx b/example/forest/t8_test_ghost.cxx index 1e053ff2c6..f4a31ae1ce 100644 --- a/example/forest/t8_test_ghost.cxx +++ b/example/forest/t8_test_ghost.cxx @@ -31,7 +31,7 @@ #include #include #include -#include +#include typedef enum { REFINE_THIRD = 0, /* Refine every third element */ diff --git a/example/remove/t8_example_gauss_blob.cxx b/example/remove/t8_example_gauss_blob.cxx index 6d9beadfae..8c5959fb3c 100644 --- a/example/remove/t8_example_gauss_blob.cxx +++ b/example/remove/t8_example_gauss_blob.cxx @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -34,11 +34,11 @@ struct t8_adapt_data const int remove_scope; const double spheres_radius_inner; const double spheres_radius_outer; - const double midpoint[3]; + const t8_3D_point midpoint; }; static double -t8_gausss_blob (const double center_elem[3], const double center_cube[3], const double radius) +t8_gausss_blob (const t8_3D_point ¢er_elem, const t8_3D_point ¢er_cube, const double radius) { double expo = 0; for (int i = 0; i < 3; i++) { @@ -49,7 +49,7 @@ t8_gausss_blob (const double center_elem[3], const double center_cube[3], const } static double * -t8_create_element_data (t8_forest_t forest, const double sphere_center[3], const double sphere_radius) +t8_create_element_data (t8_forest_t forest, const t8_3D_point &sphere_center, const double sphere_radius) { t8_locidx_t num_local_elements; t8_locidx_t num_ghost_elements; @@ -68,8 +68,8 @@ t8_create_element_data (t8_forest_t forest, const double sphere_center[3], const t8_locidx_t num_elements_in_tree = t8_forest_get_tree_num_elements (forest, itree); for (t8_locidx_t ielement = 0; ielement < num_elements_in_tree; ++ielement, ++current_index) { element = t8_forest_get_element_in_tree (forest, itree, ielement); - double center[3]; - t8_forest_element_centroid (forest, itree, element, center); + t8_3D_point center; + t8_forest_element_centroid (forest, itree, element, center.data ()); element_data[current_index] = t8_gausss_blob (center, sphere_center, sphere_radius); } } @@ -103,10 +103,10 @@ t8_adapt_refine (t8_forest_t forest, t8_forest_t forest_from, const t8_locidx_t const struct t8_adapt_data *adapt_data = (const struct t8_adapt_data *) t8_forest_get_user_data (forest); T8_ASSERT (adapt_data != NULL); - double centroid[3]; - t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid); + t8_3D_point centroid; + t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid.data ()); - const double dist = t8_vec_dist (adapt_data->midpoint, centroid); + const double dist = t8_dist (adapt_data->midpoint, centroid); if (dist < adapt_data->spheres_radius_outer) { return 1; } @@ -122,10 +122,10 @@ t8_adapt_remove (t8_forest_t forest, t8_forest_t forest_from, const t8_locidx_t const struct t8_adapt_data *adapt_data = (const struct t8_adapt_data *) t8_forest_get_user_data (forest); T8_ASSERT (adapt_data != NULL); - double centroid[3]; - t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid); + t8_3D_point centroid; + t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid.data ()); - const double dist = t8_vec_dist (adapt_data->midpoint, centroid); + const double dist = t8_dist (adapt_data->midpoint, centroid); if ((dist < adapt_data->spheres_radius_inner && adapt_data->remove_scope == 1) || (dist > adapt_data->spheres_radius_outer && adapt_data->remove_scope == 2)) { return -2; @@ -149,9 +149,11 @@ t8_construct_spheres (const int initial_level, const double radius_inner, const cmesh = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0); } + t8_3D_point midpoint ({ 0.5, 0.5, 0.5 }); + /* On each face of a cube, a sphere rises halfway in. * Its center is therefore the center of the corresponding surface. */ - struct t8_adapt_data adapt_data = { remove_scope, radius_inner, radius_outer, { 0.5, 0.5, 0.5 } }; + struct t8_adapt_data adapt_data = { remove_scope, radius_inner, radius_outer, midpoint }; forest = t8_forest_new_uniform (cmesh, t8_scheme_new_default (), initial_level, 0, sc_MPI_COMM_WORLD); forest = t8_forest_new_adapt (forest, t8_adapt_refine, 0, 0, &adapt_data); diff --git a/example/remove/t8_example_spheres.cxx b/example/remove/t8_example_spheres.cxx index ef3f178779..81bab83a4a 100644 --- a/example/remove/t8_example_spheres.cxx +++ b/example/remove/t8_example_spheres.cxx @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -34,7 +34,7 @@ struct t8_adapt_data const int num_spheres; const double spheres_radius_inner; const double spheres_radius_outer; - const double *midpoints; + std::vector midpoints; }; /* Refine, if element is within a given radius. */ @@ -46,14 +46,14 @@ t8_adapt_callback_refine (t8_forest_t forest, t8_forest_t forest_from, t8_locidx const struct t8_adapt_data *adapt_data = (const struct t8_adapt_data *) t8_forest_get_user_data (forest); T8_ASSERT (adapt_data != NULL); - double centroid[3]; - t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid); + t8_3D_point centroid; + t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid.data ()); - for (int i = 0; i < adapt_data->num_spheres; i++) { - const double dist = t8_vec_dist (adapt_data->midpoints + (i * 3), centroid); - if (dist < adapt_data->spheres_radius_outer) { - return 1; - } + auto within_radius + = [&] (const t8_3D_point &midpoint) { return t8_dist (midpoint, centroid) < adapt_data->spheres_radius_outer; }; + + if (std::any_of (adapt_data->midpoints.begin (), adapt_data->midpoints.end (), within_radius)) { + return 1; } return 0; } @@ -67,14 +67,14 @@ t8_adapt_callback_remove (t8_forest_t forest, t8_forest_t forest_from, t8_locidx const struct t8_adapt_data *adapt_data = (const struct t8_adapt_data *) t8_forest_get_user_data (forest); T8_ASSERT (adapt_data != NULL); - double centroid[3]; - t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid); + t8_3D_point centroid; + t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid.data ()); + + auto within_radius + = [&] (const t8_3D_point &midpoint) { return t8_dist (midpoint, centroid) < adapt_data->spheres_radius_inner; }; - for (int i = 0; i < adapt_data->num_spheres; i++) { - const double dist = t8_vec_dist (adapt_data->midpoints + (i * 3), centroid); - if (dist < adapt_data->spheres_radius_inner) { - return -2; - } + if (std::any_of (adapt_data->midpoints.begin (), adapt_data->midpoints.end (), within_radius)) { + return 1; } return 0; } @@ -108,8 +108,9 @@ t8_construct_spheres (const int initial_level, const double radius_inner, const /* On each face of a cube, a sphere rises halfway in. * Its center is therefore the center of the corresponding surface. */ const int num_spheres = 6; - double midpoints[6 * 3] - = { 1.0, 0.5, 0.5, 0.5, 1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 0.0, 0.5, 0.5, 0.5, 0.0 }; + std::vector midpoints + = { t8_3D_point ({ 1.0, 0.5, 0.5 }), t8_3D_point ({ 0.5, 1.0, 0.5 }), t8_3D_point ({ 0.5, 0.5, 1.0 }), + t8_3D_point ({ 0.0, 0.5, 0.5 }), t8_3D_point ({ 0.5, 0.0, 0.5 }), t8_3D_point ({ 0.5, 0.5, 0.0 }) }; struct t8_adapt_data adapt_data = { num_spheres, radius_inner, radius_outer, midpoints }; forest = t8_forest_new_uniform (cmesh, t8_scheme_new_default (), initial_level, 0, sc_MPI_COMM_WORLD); diff --git a/p4est b/p4est index 851774a0c9..8399186d4e 160000 --- a/p4est +++ b/p4est @@ -1 +1 @@ -Subproject commit 851774a0c993aaf6f1719da6fd62c2f3acd761ee +Subproject commit 8399186d4ea214361ce0d183b9bbd340a417075e diff --git a/scripts/check_macros.scp b/scripts/check_macros.scp new file mode 100755 index 0000000000..549b75e78f --- /dev/null +++ b/scripts/check_macros.scp @@ -0,0 +1,38 @@ +#!/bin/bash + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +file_path=$1 + +# +# This script searches for lines containing a macro definition in the style of '#ifdef T8_ENABLE_' +# in the specified file and processes each matching line. +# It uses 'grep' to find all occurrences of '#ifdef T8_ENABLE_' in the file located +# at the path stored in the variable 'file_path'. The '-n' option with 'grep' +# ensures that the line numbers of the matching lines are included in the output. +# The output of 'grep' is then piped into a 'while' loop, which reads each line +# and splits it into the line number and the line content using ':' as the delimiter. +# Variables: +# - file_path: The path to the file to be searched. +# - line_number: The line number where the macro definition is found. +# - line: The content of the line where the macro definition is found. +# + +found_macros=FALSE + +while IFS=: read -r line_number line; do + macro_name=$(echo "$line" | grep -o 'T8_ENABLE_[^ ]*') + echo "Macro found in $file_path on line $line_number: $macro_name" + found_macros=TRUE +done < <(grep -n '#ifdef T8_ENABLE_' "$file_path") + +if [ "$found_macros" = "TRUE" ]; then + echo "Incorrect macro usage found in $file_path. Please use '#if T8_ENABLE_' instead." + exit 1 +else + echo "No incorrect macro usage found in $file_path." + exit 0 +fi diff --git a/scripts/pre-commit b/scripts/pre-commit index 63430b9282..3308a4605b 100755 --- a/scripts/pre-commit +++ b/scripts/pre-commit @@ -34,6 +34,8 @@ # This is the indent script in the project's directory CHECK_INDENT=./scripts/check_if_file_indented.scp +CHECK_MACROS=./scripts/check_makros.scp + TYPOS=`which typos 2> /dev/null` TYPOS_CONFIG_FILE=./.typos.toml @@ -92,6 +94,19 @@ do echo "File $file is not indented." nocontinue=1 fi + + # This script checks for the usage of #ifdef T8_ENABLE_ macros in the specified file. + # If such macros are found, it suggests using #if T8_ENABLE_ instead and sets a flag to indicate the issue. + # - $CHECK_MACROS: Command or script to check the macros in the file. + # - $file: The file being checked. + # - status: The exit status of the $CHECK_MACROS command. + # - nocontinue: Flag set to 1 if the incorrect macro usage is found. + $CHECK_MACROS $file + status=$? + if test $status -ne 0 + then + nocontinue=1 + fi fi done diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb7e547a4a..4f6289b754 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,14 +60,10 @@ endif() if( T8CODE_ENABLE_OCC ) target_compile_definitions( T8 PUBLIC T8_WITH_OCC=1 ) - target_include_directories( T8 PUBLIC ${OpenCASCADE_INCLUDE_DIR} ) + target_include_directories( T8 SYSTEM PUBLIC ${OpenCASCADE_INCLUDE_DIR} ) target_link_libraries( T8 PUBLIC ${OpenCASCADE_LIBRARIES} ) endif() -if( T8CODE_ENABLE_LESS_TESTS ) - target_compile_definitions( T8 PUBLIC T8_ENABLE_LESS_TESTS=1 ) -endif() - if( T8CODE_BUILD_PEDANTIC ) target_compile_options( T8 PUBLIC -pedantic ) set (T8_CXXFLAGS "${T8_CXXFLAGS} -Wpedantic") @@ -125,7 +121,8 @@ target_sources( T8 PRIVATE t8_forest/t8_forest_ghost.cxx t8_forest/t8_forest_iterate.cxx t8_forest/t8_forest_balance.cxx - t8_forest/t8_forest_netcdf.cxx + t8_forest/t8_forest_netcdf.cxx + t8_forest/t8_forest_search/t8_forest_search.cxx t8_geometry/t8_geometry.cxx t8_geometry/t8_geometry_helpers.c t8_geometry/t8_geometry_base.cxx @@ -158,7 +155,8 @@ target_sources( T8 PRIVATE t8_schemes/t8_default/t8_default_tri/t8_dtri_bits.c t8_schemes/t8_default/t8_default_tri/t8_dtri_connectivity.c t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.cxx - t8_schemes/t8_default/t8_default_vertex/t8_dvertex_bits.c + t8_types/t8_vec.cxx + t8_schemes/t8_standalone/t8_standalone.cxx t8_vtk/t8_vtk_polydata.cxx t8_vtk/t8_vtk_unstructured.cxx t8_vtk/t8_vtk_parallel.cxx @@ -203,29 +201,30 @@ install( FILES t8_mesh.h t8_netcdf.h t8_refcount.h - t8_vec.h + t8_types/t8_vec.hxx t8_version.h t8_vtk.h - t8_windows.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include + t8_windows.h DESTINATION include ) -install( DIRECTORY t8_cmesh DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.h" ) -install( DIRECTORY t8_data DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.h" ) -install( DIRECTORY t8_forest DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING +install( DIRECTORY t8_cmesh DESTINATION include FILES_MATCHING PATTERN "*.h" ) +install( DIRECTORY t8_data DESTINATION include FILES_MATCHING PATTERN "*.h" ) +install( DIRECTORY t8_forest DESTINATION include FILES_MATCHING PATTERN "*.h" PATTERN "*private.h" EXCLUDE ) -install( DIRECTORY t8_geometry DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.h" ) -install( DIRECTORY t8_schemes DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.h" ) -install( DIRECTORY t8_vtk DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.h" ) - -install( DIRECTORY t8_cmesh DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.hxx" ) -install( DIRECTORY t8_data DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.hxx" ) -install( DIRECTORY t8_forest DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.hxx" ) -install( DIRECTORY t8_geometry DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.hxx" ) -install( DIRECTORY t8_schemes DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.hxx" ) -install( DIRECTORY t8_vtk DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.hxx" ) - -install( TARGETS T8 DESTINATION ${CMAKE_INSTALL_PREFIX}/lib ) +install( DIRECTORY t8_geometry DESTINATION include FILES_MATCHING PATTERN "*.h" ) +install( DIRECTORY t8_schemes DESTINATION include FILES_MATCHING PATTERN "*.h" ) +install( DIRECTORY t8_vtk DESTINATION include FILES_MATCHING PATTERN "*.h" ) + +install( DIRECTORY t8_cmesh DESTINATION include FILES_MATCHING PATTERN "*.hxx" ) +install( DIRECTORY t8_data DESTINATION include FILES_MATCHING PATTERN "*.hxx" ) +install( DIRECTORY t8_forest DESTINATION include FILES_MATCHING PATTERN "*.hxx" ) +install( DIRECTORY t8_geometry DESTINATION include FILES_MATCHING PATTERN "*.hxx" ) +install( DIRECTORY t8_schemes DESTINATION include FILES_MATCHING PATTERN "*.hxx" ) +install( DIRECTORY t8_vtk DESTINATION include FILES_MATCHING PATTERN "*.hxx" ) +install( DIRECTORY t8_types DESTINATION include FILES_MATCHING PATTERN "*.hxx" ) + +install( TARGETS T8 DESTINATION lib ) install( TARGETS T8 EXPORT ${PROJECT_NAME}-targets ) include( CMakePackageConfigHelpers ) diff --git a/src/Makefile.am b/src/Makefile.am index 37c558db43..965cfacbc8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,7 +27,7 @@ libt8_installed_headers = \ src/t8_refcount.h src/t8_cmesh.hxx src/t8_cmesh.h src/t8_cmesh_triangle.h \ src/t8_cmesh_tetgen.h src/t8_cmesh_readmshfile.h \ src/t8_cmesh_vtk_reader.hxx \ - src/t8_vec.h \ + src/t8_vec.hxx \ src/t8_mat.h \ src/t8_version.h \ src/t8_vtk.h \ @@ -53,7 +53,9 @@ libt8_installed_headers_forest = \ src/t8_forest/t8_forest_profiling.h \ src/t8_forest/t8_forest_io.h \ src/t8_forest/t8_forest_adapt.h \ - src/t8_forest/t8_forest_iterate.h src/t8_forest/t8_forest_partition.h + src/t8_forest/t8_forest_iterate.h \ + src/t8_forest/t8_forest_partition.h \ + src/t8_forest/t8_forest_search/t8_forest_search.hxx libt8_installed_headers_geometry = \ src/t8_geometry/t8_geometry.h \ src/t8_geometry/t8_geometry_handler.hxx \ @@ -111,6 +113,7 @@ libt8_internal_headers = \ libt8_compiled_sources = \ src/t8.c src/t8_eclass.c src/t8_mesh.c \ src/t8_element.cxx \ + src/t8_vec.cxx \ src/t8_element_c_interface.cxx \ src/t8_refcount.c src/t8_cmesh/t8_cmesh.cxx \ src/t8_cmesh/t8_cmesh_cad.cxx src/t8_cmesh/t8_cmesh_triangle.cxx \ @@ -145,6 +148,7 @@ libt8_compiled_sources = \ src/t8_forest/t8_forest_partition.cxx src/t8_forest/t8_forest.cxx \ src/t8_forest/t8_forest_private.c \ src/t8_forest/t8_forest_ghost.cxx src/t8_forest/t8_forest_iterate.cxx \ + src/t8_forest/t8_forest_search/t8_forest_search.cxx \ src/t8_version.c \ src/t8_vtk.c src/t8_forest/t8_forest_balance.cxx \ src/t8_forest/t8_forest_netcdf.cxx \ diff --git a/src/t8_cmesh.h b/src/t8_cmesh.h index da5d48e101..ea1043ca8f 100644 --- a/src/t8_cmesh.h +++ b/src/t8_cmesh.h @@ -769,11 +769,6 @@ t8_cmesh_unref (t8_cmesh_t *pcmesh); void t8_cmesh_destroy (t8_cmesh_t *pcmesh); -/* Functions for constructing complete and committed cmeshes */ - -t8_cmesh_t -t8_cmesh_new_testhybrid (sc_MPI_Comm comm); - /** Compute y = ax + b on an array of doubles, interpreting * each 3 as one vector x * \param[in] coords_in The incoming coordinates of the vectors diff --git a/src/t8_cmesh.hxx b/src/t8_cmesh.hxx index ed428b0089..238dd64fcd 100644 --- a/src/t8_cmesh.hxx +++ b/src/t8_cmesh.hxx @@ -24,7 +24,8 @@ * We define the coarse mesh of trees in this file. */ -#pragma once +#ifndef T8_CMESH_HXX +#define T8_CMESH_HXX #include #include @@ -48,3 +49,5 @@ t8_cmesh_register_geometry (t8_cmesh_t cmesh, _args &&...args) } return cmesh->geometry_handler->register_geometry (std::forward<_args> (args)...); } + +#endif /* T8_CMESH_HXX */ diff --git a/src/t8_cmesh/t8_cmesh.cxx b/src/t8_cmesh/t8_cmesh.cxx index cf85f7e2d2..1a517a19ee 100644 --- a/src/t8_cmesh/t8_cmesh.cxx +++ b/src/t8_cmesh/t8_cmesh.cxx @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include "t8_cmesh_types.h" #ifdef T8_WITH_METIS @@ -581,9 +581,9 @@ t8_cmesh_tree_vertices_negative_volume (const t8_eclass_t eclass, const double * v_j[2] = 1.0; /* Compute cross = v_1 x v_2. */ - t8_vec_cross (v_1, v_2, cross); + t8_cross_3D (v_1, v_2, cross); /* Compute sc_prod = . */ - sc_prod = t8_vec_dot (v_j, cross); + sc_prod = t8_dot (v_j, cross); T8_ASSERT (sc_prod != 0); return sc_prod < 0; @@ -604,9 +604,9 @@ t8_cmesh_tree_vertices_negative_volume (const t8_eclass_t eclass, const double * v_j[i] = vertices[3 * j + i] - vertices[i]; } /* compute cross = v_1 x v_2 */ - t8_vec_cross (v_1, v_2, cross); + t8_cross_3D (v_1, v_2, cross); /* Compute sc_prod = */ - sc_prod = t8_vec_dot (v_j, cross); + sc_prod = t8_dot (v_j, cross); T8_ASSERT (sc_prod != 0); return eclass == T8_ECLASS_TET ? sc_prod > 0 : sc_prod < 0; @@ -1337,7 +1337,7 @@ t8_cmesh_coords_axb (const double *coords_in, double *coords_out, int num_vertic int i; for (i = 0; i < num_vertices; i++) { - t8_vec_axpyz (coords_in + i * 3, b, coords_out + i * 3, alpha); + t8_axpyz (coords_in + i * 3, b, coords_out + i * 3, alpha); } } diff --git a/src/t8_cmesh/t8_cmesh_examples.cxx b/src/t8_cmesh/t8_cmesh_examples.cxx index 6305792407..49d2390b1e 100644 --- a/src/t8_cmesh/t8_cmesh_examples.cxx +++ b/src/t8_cmesh/t8_cmesh_examples.cxx @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -680,12 +680,6 @@ t8_cmesh_new_hypercube (t8_eclass_t eclass, sc_MPI_Comm comm, int do_bcast, int SC_CHECK_ABORT (eclass != T8_ECLASS_PYRAMID || !periodic, "The pyramid cube mesh cannot be periodic.\n"); - if (do_partition) { - t8_global_errorf ( - "WARNING: Partitioning the hypercube cmesh is currently not supported.\n" - "Using this cmesh will crash when vertices are used. See also https://github.com/DLR-AMR/t8code/issues/79\n"); - } - mpiret = sc_MPI_Comm_rank (comm, &mpirank); SC_CHECK_MPI (mpiret); if (!do_bcast || mpirank == 0) { @@ -874,14 +868,14 @@ t8_update_box_face_edges (const int dim, const double *box_corners, double *box_ const double *v_1 = box_corners + (t8_edge_vertex_to_tree_vertex[eclass][edge][0] * 3); const double *v_2 = box_corners + (t8_edge_vertex_to_tree_vertex[eclass][edge][1] * 3); /* Get the direction vector between v_1 and v_2 and store it in box_dir. */ - t8_vec_axpyz (v_1, v_2, box_dir + (edge * 3), -1.0); + t8_axpyz (v_1, v_2, box_dir + (edge * 3), -1.0); /* Get number of quads or hexs along current edge. */ const double num_cubes = eclass == T8_ECLASS_QUAD ? (double) axes[(edge / 2 + 1) % 2] : (double) axes[edge / 4]; /* Set length of directional vector to length of one quad or hex. */ double length_edge; - length_edge = t8_vec_norm (box_dir + (edge * 3)) * num_cubes; - length_edge = t8_vec_dist (v_1, v_2) / length_edge; - t8_vec_ax (box_dir + (edge * 3), length_edge); + length_edge = t8_norm (box_dir + (edge * 3)) * num_cubes; + length_edge = t8_dist (v_1, v_2) / length_edge; + t8_ax (box_dir + (edge * 3), length_edge); } } @@ -906,7 +900,7 @@ t8_resize_box (const int dim, double *box_corners, const double *box_dir, const for (int face_corner = 0; face_corner < num_face_corner; face_corner++) { const int box_vertex = t8_face_vertex_to_tree_vertex[eclass][face][face_corner]; const int box_edge = t8_face_to_edge_neighbor[eclass][face][face_corner]; - t8_vec_axpy (box_dir + (box_edge * 3), box_corners + (box_vertex * 3), (double) factor); + t8_axpy (box_dir + (box_edge * 3), box_corners + (box_vertex * 3), (double) factor); } axes[face / 2] += face % 2 ? factor : -factor; } @@ -975,15 +969,15 @@ t8_cmesh_set_vertices_2D (t8_cmesh_t cmesh, const t8_eclass_t eclass, const doub */ for (t8_locidx_t quad_y_id = 0; quad_y_id < quads_y; quad_y_id++) { for (t8_locidx_t quad_x_id = 0; quad_x_id < quads_x; quad_x_id++) { - memcpy (vertices, box, 3 * sizeof (double)); /* Vertex 0 */ - t8_vec_axpyz (box, box_dir, vertices + 6, 1.0); /* Vertex 2 */ + memcpy (vertices, box, 3 * sizeof (double)); /* Vertex 0 */ + t8_axpyz (box, box_dir, vertices + 6, 1.0); /* Vertex 2 */ /* Reduce box along x axis */ t8_resize_box (2, box, box_dir, 0, 1, box_quads); t8_update_box_face_edges (2, box, box_dir, 0, box_quads); - t8_vec_axy (box, vertices + 3, 1.0); /* Vertex 1 */ - t8_vec_axpyz (box, box_dir, vertices + 9, 1.0); /* Vertex 3 */ + t8_axy (box, vertices + 3, 1.0); /* Vertex 1 */ + t8_axpyz (box, box_dir, vertices + 9, 1.0); /* Vertex 3 */ if (use_axis_aligned_geom && eclass == T8_ECLASS_QUAD) { /* Copy vertex 3 into the place of vertex 1. The box-procedure has to be done to compute * vertex 3 correctly. */ @@ -1124,29 +1118,29 @@ t8_cmesh_set_vertices_3D (t8_cmesh_t cmesh, const t8_eclass_t eclass, const doub for (t8_locidx_t hex_z_id = 0; hex_z_id < hexs_z; hex_z_id++) { for (t8_locidx_t hex_y_id = 0; hex_y_id < hexs_y; hex_y_id++) { for (t8_locidx_t hex_x_id = 0; hex_x_id < hexs_x; hex_x_id++) { - memcpy (vertices, box, 3 * sizeof (double)); /* Vertex 0 */ - t8_vec_axpyz (box, box_dir + 12, vertices + 6, 1.0); /* Vertex 2 */ + memcpy (vertices, box, 3 * sizeof (double)); /* Vertex 0 */ + t8_axpyz (box, box_dir + 12, vertices + 6, 1.0); /* Vertex 2 */ /* Reduce box along z axis and face 4. */ t8_resize_box (3, box, box_dir, 4, 1, box_hexs); t8_update_box_face_edges (3, box, box_dir, 4, box_hexs); - t8_vec_axy (box, vertices + 12, 1.0); /* Vertex 4 */ - t8_vec_axpyz (box, box_dir + 12, vertices + 18, 1.0); /* Vertex 6 */ + t8_axy (box, vertices + 12, 1.0); /* Vertex 4 */ + t8_axpyz (box, box_dir + 12, vertices + 18, 1.0); /* Vertex 6 */ /* Reduce box along x axis and face 0. */ t8_resize_box (3, box, box_dir, 0, 1, box_hexs); t8_update_box_face_edges (3, box, box_dir, 0, box_hexs); - t8_vec_axy (box, vertices + 15, 1.0); /* Vertex 5 */ - t8_vec_axpyz (box, box_dir + 12, vertices + 21, 1.0); /* Vertex 7 */ + t8_axy (box, vertices + 15, 1.0); /* Vertex 5 */ + t8_axpyz (box, box_dir + 12, vertices + 21, 1.0); /* Vertex 7 */ /* Increase box along z axis and and face 4 */ t8_resize_box (3, box, box_dir, 4, -1, box_hexs); t8_update_box_face_edges (3, box, box_dir, 4, box_hexs); - t8_vec_axy (box, vertices + 3, 1.0); /* Vertex 1 */ - t8_vec_axpyz (box, box_dir + 12, vertices + 9, 1.0); /* Vertex 3 */ + t8_axy (box, vertices + 3, 1.0); /* Vertex 1 */ + t8_axpyz (box, box_dir + 12, vertices + 9, 1.0); /* Vertex 3 */ if (use_axis_aligned_geom && eclass == T8_ECLASS_HEX) { /* Copy vertex 7 into the place of vertex 1. The box-procedure has to be done to compute @@ -1319,24 +1313,24 @@ t8_cmesh_new_hypercube_pad_ext (const t8_eclass_t eclass, sc_MPI_Comm comm, cons T8_ASSERT (boundary[3] > boundary[0]); /* Get the direction of the line */ double line_dir[3]; - t8_vec_axpyz (boundary, boundary + 3, line_dir, -1.0); + t8_axpyz (boundary, boundary + 3, line_dir, -1.0); /* Get length of one tree */ double length; - length = t8_vec_norm (line_dir) * (double) polygons_x; - length = t8_vec_dist (boundary, boundary + 3) / length; - t8_vec_ax (line_dir, length); + length = t8_norm (line_dir) * (double) polygons_x; + length = t8_dist (boundary, boundary + 3) / length; + t8_ax (line_dir, length); double vertices[6]; /* Set first vertex to lower end of line */ memcpy (vertices, boundary, 3 * sizeof (double)); /* Set second vertex to lower end of line + line_dir */ - t8_vec_axpyz (line_dir, boundary, vertices + 3, 1.0); + t8_axpyz (line_dir, boundary, vertices + 3, 1.0); for (t8_gloidx_t tree_x = 0; tree_x < polygons_x; tree_x++) { t8_cmesh_set_tree_vertices (cmesh, tree_x + offset, vertices, 2); /* Update vertices for next tree */ - t8_vec_axy (vertices, vertices + 3, 1.0); - t8_vec_axpy (line_dir, vertices + 3, 1.0); + t8_axy (vertices, vertices + 3, 1.0); + t8_axpy (line_dir, vertices + 3, 1.0); } } else { @@ -1344,7 +1338,7 @@ t8_cmesh_new_hypercube_pad_ext (const t8_eclass_t eclass, sc_MPI_Comm comm, cons T8_ASSERT (eclass == T8_ECLASS_VERTEX); double vertex[3]; /* Vertex == boundary. */ - t8_vec_axy (boundary, vertex, 1.0); + t8_axy (boundary, vertex, 1.0); t8_cmesh_set_tree_vertices (cmesh, offset, vertex, 1); } @@ -3372,10 +3366,10 @@ t8_cmesh_new_spherical_shell (t8_eclass_t eclass, t8_geometry_c *geometry, * Note, this works for triangles and quads. */ double normal[3]; - t8_vec_tri_normal (elem_vertices_2d, elem_vertices_2d + 3, elem_vertices_2d + 6, normal); + t8_normal_of_tri (elem_vertices_2d, elem_vertices_2d + 3, elem_vertices_2d + 6, normal); - if (t8_vec_dot (elem_vertices_2d, normal) < 0.0) { - t8_vec_swap (elem_vertices_2d + 3, elem_vertices_2d + 6); + if (t8_dot (elem_vertices_2d, normal) < 0.0) { + t8_swap (elem_vertices_2d + 3, elem_vertices_2d + 6); } } diff --git a/src/t8_cmesh/t8_cmesh_examples.h b/src/t8_cmesh/t8_cmesh_examples.h index c5937e347e..188a4cdbe0 100644 --- a/src/t8_cmesh/t8_cmesh_examples.h +++ b/src/t8_cmesh/t8_cmesh_examples.h @@ -102,7 +102,7 @@ t8_cmesh_new_hypercube (t8_eclass_t eclass, sc_MPI_Comm comm, int do_bcast, int * \param [in] polygons_x The number of polygons along the x-axis. * \param [in] polygons_y The number of polygons along the y-axis. * Only required if \a eclass is 2D or 3D. - * \param [in] polygons_z The number of polygons along the z-axis. + * \param [in] polygons_z The number of polygons along the z-axis. * Only required if \a eclass is 3D. * \param [in] use_axis_aligned Use the axis-aligned geometry. If used, only two points per tree are stored. * \return A committed t8_cmesh structure with @@ -140,7 +140,7 @@ t8_cmesh_new_hypercube_pad (const t8_eclass_t eclass, sc_MPI_Comm comm, const do * \param [in] polygons_x The number of polygons along the x-axis. * \param [in] polygons_y The number of polygons along the y-axis. * Only required if \a eclass is 2D or 3D. - * \param [in] polygons_z The number of polygons along the z-axis. + * \param [in] polygons_z The number of polygons along the z-axis. * Only required if \a eclass is 3D. * \param [in] periodic_x Connect opposite sides of the hypercube in x-direction. * \param [in] periodic_y Connect opposite sides of the hypercube in y-direction. diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 3b1f8bac36..eb55a9f081 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -480,7 +480,7 @@ t8_forest_element_diam (t8_forest_t forest, t8_locidx_t ltreeid, const t8_elemen /* Compute coordinates of this corner */ t8_forest_element_coordinate (forest, ltreeid, element, i, coordinates); /* Compute the distance to the midpoint */ - dist += t8_vec_dist (coordinates, centroid); + dist += t8_dist (coordinates, centroid); } /* We approximate the diameter as twice the average of the distances @@ -519,7 +519,7 @@ t8_forest_element_line_length (t8_forest_t forest, t8_locidx_t ltreeid, const t8 t8_forest_element_coordinate (forest, ltreeid, element, corner_b, coordinates_b); /* Compute the euclidean distance */ - length = t8_vec_dist (coordinates_a, coordinates_b); + length = t8_dist (coordinates_a, coordinates_b); /* return it */ return length; } @@ -532,13 +532,13 @@ t8_forest_element_triangle_area (double coordinates[3][3]) /* Compute vectors v_1 and v_2 */ /* v_1 = v_1 - v_0 */ - t8_vec_axpy (coordinates[0], coordinates[1], -1); + t8_axpy (coordinates[0], coordinates[1], -1); /* v_2 = v_2 - v_0 */ - t8_vec_axpy (coordinates[0], coordinates[2], -1); + t8_axpy (coordinates[0], coordinates[2], -1); /* compute scalar products */ - v_1v_1 = t8_vec_dot (coordinates[1], coordinates[1]); - v_1v_2 = t8_vec_dot (coordinates[1], coordinates[2]); - v_2v_2 = t8_vec_dot (coordinates[2], coordinates[2]); + v_1v_1 = t8_dot (coordinates[1], coordinates[1]); + v_1v_2 = t8_dot (coordinates[1], coordinates[2]); + v_2v_2 = t8_dot (coordinates[2], coordinates[2]); /* compute determinant and half it */ return 0.5 * sqrt (fabs (v_1v_1 * v_2v_2 - v_1v_2 * v_1v_2)); @@ -560,14 +560,14 @@ t8_forest_element_tet_volume (const double coordinates[4][3]) /* subtract the 4-th vector from the other 3 */ for (i = 0; i < 3; i++) { - t8_vec_axpyz (coordinates[3], coordinates[i], coordinates_tmp[i], -1); + t8_axpyz (coordinates[3], coordinates[i], coordinates_tmp[i], -1); } /* Compute the cross product of the 2nd and 3rd */ - t8_vec_cross (coordinates_tmp[1], coordinates_tmp[2], cross); + t8_cross_3D (coordinates_tmp[1], coordinates_tmp[2], cross); /* return |(a-d) * ((b-d)x(c-d))| / 6 */ - return fabs (t8_vec_dot (coordinates_tmp[0], cross)) / 6; + return fabs (t8_dot (coordinates_tmp[0], cross)) / 6; } /* Compute an element's volume */ @@ -668,14 +668,14 @@ t8_forest_element_volume (t8_forest_t forest, t8_locidx_t ltreeid, const t8_elem /* Compute the difference of each corner with corner 0 */ for (i = 1; i < 4; i++) { - t8_vec_axpy (coordinates[0], coordinates[i], -1); + t8_axpy (coordinates[0], coordinates[i], -1); } /* Compute the cross product of the 2nd and 3rd */ - t8_vec_cross (coordinates[2], coordinates[3], cross); + t8_cross_3D (coordinates[2], coordinates[3], cross); /* return |(a-d) * ((b-d)x(c-d))| */ - return fabs (t8_vec_dot (coordinates[1], cross)); + return fabs (t8_dot (coordinates[1], cross)); } case T8_ECLASS_PRISM: @@ -835,9 +835,9 @@ t8_forest_element_face_centroid (t8_forest_t forest, t8_locidx_t ltreeid, const /* Compute the average of those coordinates */ /* centroid = centroid + vertex_a */ - t8_vec_axpy (vertex_a, centroid, 1); + t8_axpy (vertex_a, centroid, 1); /* centroid /= 2 */ - t8_vec_ax (centroid, 0.5); + t8_ax (centroid, 0.5); return; } break; case T8_ECLASS_TRIANGLE: @@ -854,12 +854,12 @@ t8_forest_element_face_centroid (t8_forest_t forest, t8_locidx_t ltreeid, const for (i = 1; i < num_corners; i++) { /* coordinates[0] = SUM (coordinates[i]) */ - t8_vec_axpy (coordinates[i], coordinates[0], 1); + t8_axpy (coordinates[i], coordinates[0], 1); } /* centroid = coordinates[0] */ - t8_vec_axb (coordinates[0], centroid, 1, 0); + t8_axb (coordinates[0], centroid, 1, 0); /* divide by num corners */ - t8_vec_ax (centroid, 1. / num_corners); + t8_ax (centroid, 1. / num_corners); return; } break; default: @@ -885,30 +885,30 @@ t8_four_points_coplanar (const double p_0[3], const double p_1[3], const double /* A = p1 - p0 */ double A[3]; - t8_vec_axpyz (p_0, p_1, A, -1); + t8_axpyz (p_0, p_1, A, -1); /* B = p2 - p0 */ double B[3]; - t8_vec_axpyz (p_0, p_2, B, -1); + t8_axpyz (p_0, p_2, B, -1); /* C = p3 - p0 */ double C[3]; - t8_vec_axpyz (p_0, p_3, C, -1); + t8_axpyz (p_0, p_3, C, -1); /* n1 = A x B */ double A_cross_B[3]; - t8_vec_cross (A, B, A_cross_B); + t8_cross_3D (A, B, A_cross_B); /* n2 = A x C */ double A_cross_C[3]; - t8_vec_cross (A, C, A_cross_C); + t8_cross_3D (A, C, A_cross_C); /* n1 x n2 */ double n1_cross_n2[3]; - t8_vec_cross (A_cross_B, A_cross_C, n1_cross_n2); + t8_cross_3D (A_cross_B, A_cross_C, n1_cross_n2); /* || n1 x n2 || */ - const double norm = t8_vec_norm (n1_cross_n2); + const double norm = t8_norm (n1_cross_n2); return norm < tolerance; } #endif @@ -941,16 +941,16 @@ t8_forest_element_face_normal (t8_forest_t forest, t8_locidx_t ltreeid, const t8 t8_forest_element_coordinate (forest, ltreeid, element, 1, normal); /* Compute normal = v_1 - v_0 */ - t8_vec_axpy (v_0, normal, -1); + t8_axpy (v_0, normal, -1); /* Compute the norm */ - norm = t8_vec_norm (normal); + norm = t8_norm (normal); /* Compute normal = normal/norm if face = 1 * normal = -normal/norm if face = 0 */ sign = face == 0 ? -1 : 1; - t8_vec_ax (normal, sign / norm); + t8_ax (normal, sign / norm); return; case T8_ECLASS_LINE: { @@ -986,28 +986,28 @@ t8_forest_element_face_normal (t8_forest_t forest, t8_locidx_t ltreeid, const t8 * Compute the dot products */ vb_vb = c_vb = 0; /* vertex_b = vertex_b - vertex_a */ - t8_vec_axpy (vertex_a, vertex_b, -1); + t8_axpy (vertex_a, vertex_b, -1); /* center = center - vertex_a */ - t8_vec_axpy (vertex_a, center, -1); + t8_axpy (vertex_a, center, -1); /* vertex_b * vertex_b */ - vb_vb = t8_vec_dot (vertex_b, vertex_b); + vb_vb = t8_dot (vertex_b, vertex_b); /* center * vertex_b */ - c_vb = t8_vec_dot (center, vertex_b); + c_vb = t8_dot (center, vertex_b); /* Compute N = C - / V * compute the norm of N * compute N*C */ - t8_vec_axpyz (vertex_b, center, normal, -1 * c_vb / vb_vb); - norm = t8_vec_norm (normal); + t8_axpyz (vertex_b, center, normal, -1 * c_vb / vb_vb); + norm = t8_norm (normal); T8_ASSERT (norm != 0); - c_n = t8_vec_dot (center, normal); + c_n = t8_dot (center, normal); /* If N*C > 0 then N points inwards, so we have to reverse it */ if (c_n > 0) { norm *= -1; } /* divide normal by its normal to normalize it */ - t8_vec_ax (normal, 1. / norm); + t8_ax (normal, 1. / norm); return; } break; @@ -1052,26 +1052,26 @@ t8_forest_element_face_normal (t8_forest_t forest, t8_locidx_t ltreeid, const t8 t8_forest_element_coordinate (forest, ltreeid, element, corner, corner_vertices[i]); } /* Subtract vertex 0 from the other two */ - t8_vec_axpy (corner_vertices[0], corner_vertices[1], -1); - t8_vec_axpy (corner_vertices[0], corner_vertices[2], -1); + t8_axpy (corner_vertices[0], corner_vertices[1], -1); + t8_axpy (corner_vertices[0], corner_vertices[2], -1); /* Compute the cross product of the two, * and the norm of the cross product */ - t8_vec_cross (corner_vertices[1], corner_vertices[2], normal); - norm = t8_vec_norm (normal); + t8_cross_3D (corner_vertices[1], corner_vertices[2], normal); + norm = t8_norm (normal); T8_ASSERT (norm > 1e-14); /* Compute the coordinates of the center of the element */ t8_forest_element_centroid (forest, ltreeid, element, center); /* Compute center = center - vertex_0 */ - t8_vec_axpy (corner_vertices[0], center, -1); + t8_axpy (corner_vertices[0], center, -1); /* Compute the dot-product of normal and center */ - c_n = t8_vec_dot (center, normal); + c_n = t8_dot (center, normal); /* if c_n is positive, the computed normal points inwards, so we have to reverse it */ if (c_n > 0) { norm = -norm; } /* Divide normal by norm to normalize it */ - t8_vec_ax (normal, 1. / norm); + t8_ax (normal, 1. / norm); } break; default: SC_ABORT ("Not implemented.\n"); @@ -3685,7 +3685,6 @@ t8_eclass_t t8_forest_get_tree_class (const t8_forest_t forest, const t8_locidx_t ltreeid) { t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (forest); - T8_ASSERT (0 <= ltreeid && ltreeid < num_local_trees + t8_forest_get_num_ghost_trees (forest)); if (ltreeid < num_local_trees) { /* The id belongs to a local tree */ diff --git a/src/t8_forest/t8_forest_general.h b/src/t8_forest/t8_forest_general.h index 1dcb4c3f35..65cd17f38c 100644 --- a/src/t8_forest/t8_forest_general.h +++ b/src/t8_forest/t8_forest_general.h @@ -61,10 +61,10 @@ T8_EXTERN_C_BEGIN (); * of the new forest that are either refined, coarsened or the same as elements in the old forest. * * \param [in] forest_old The forest that is adapted - * \param [in] forest_new The forest that is newly constructed from \a forest_old + * \param [in, out] forest_new The forest that is newly constructed from \a forest_old * \param [in] which_tree The local tree containing \a first_outgoing and \a first_incoming * \param [in] tree_class The eclass of the local tree containing \a first_outgoing and \a first_incoming - * \param [in] scheme The scheme of the forest + * \param [in] scheme The scheme of the forest * \param [in] refine -1 if family in \a forest_old got coarsened, 0 if element * has not been touched, 1 if element got refined and -2 if * element got removed. See return of t8_forest_adapt_t. @@ -101,7 +101,7 @@ typedef void (*t8_forest_replace_t) (t8_forest_t forest_old, t8_forest_t forest_ * \param [in] which_tree The local tree containing \a elements. * \param [in] tree_class The eclass of \a which_tree. * \param [in] lelement_id The local element id in \a forest_old in the tree of the current element. - * \param [in] scheme The scheme of the forest. + * \param [in] scheme The scheme of the forest. * \param [in] is_family If 1, the first \a num_elements entries in \a elements form a family. If 0, they do not. * \param [in] num_elements The number of entries in \a elements that are defined * \param [in] elements Pointers to a family or, if \a is_family is zero, @@ -119,7 +119,7 @@ typedef int (*t8_forest_adapt_t) (t8_forest_t forest, t8_forest_t forest_from, t /** Create a new forest with reference count one. * This forest needs to be specialized with the t8_forest_set_* calls. - * Currently it is manatory to either call the functions \ref + * Currently it is mandatory to either call the functions \ref * t8_forest_set_mpicomm, \ref t8_forest_set_cmesh, and \ref t8_forest_set_scheme, * or to call one of \ref t8_forest_set_copy, \ref t8_forest_set_adapt, or * \ref t8_forest_set_partition. It is illegal to mix these calls, or to @@ -249,7 +249,7 @@ t8_forest_set_copy (t8_forest_t forest, const t8_forest_t from); * and it is recursive otherwise. * \note This setting can be combined with \ref t8_forest_set_partition and \ref * t8_forest_set_balance. The order in which these operations are executed is always - * 1) Adapt 2) Balance 3) Partition + * 1) Adapt 2) Partition 3) Balance. * \note This setting may not be combined with \ref t8_forest_set_copy and overwrites * this setting. */ @@ -312,7 +312,7 @@ t8_forest_get_user_function (const t8_forest_t forest); * operation. * \note This setting can be combined with \ref t8_forest_set_adapt and \ref * t8_forest_set_balance. The order in which these operations are executed is always - * 1) Adapt 2) Balance 3) Partition + * 1) Adapt 2) Partition 3) Balance. * If \ref t8_forest_set_balance is called with the \a no_repartition parameter set as * false, it is not necessary to call \ref t8_forest_set_partition additionally. * \note This setting may not be combined with \ref t8_forest_set_copy and overwrites @@ -339,8 +339,8 @@ t8_forest_set_partition (t8_forest_t forest, const t8_forest_t set_from, int set * If \a no_repartition is false, an additional call of \ref t8_forest_set_partition is not * necessary. * \note This setting can be combined with \ref t8_forest_set_adapt and \ref - * t8_forest_set_balance. The order in which these operations are executed is always - * 1) Adapt 2) Balance 3) Partition. + * t8_forest_set_partition. The order in which these operations are executed is always + * 1) Adapt 2) Partition 3) Balance. * \note This setting may not be combined with \ref t8_forest_set_copy and overwrites * this setting. */ @@ -615,8 +615,8 @@ t8_forest_leaf_face_neighbors_ext (t8_forest_t forest, t8_locidx_t ltreeid, cons t8_gloidx_t *gneigh_tree, int *orientation); /** Exchange ghost information of user defined element data. - * \param[in] forest The forest. Must be committed. - * \param[in] element_data An array of length num_local_elements + num_ghosts + * \param [in] forest The forest. Must be committed. + * \param [in] element_data An array of length num_local_elements + num_ghosts * storing one value for each local element and ghost in \a forest. * After calling this function the entries for the ghost elements * are update with the entries in the \a element_data array of diff --git a/src/t8_forest/t8_forest_ghost.cxx b/src/t8_forest/t8_forest_ghost.cxx index 7cbbde1edc..ccff9ea6c1 100644 --- a/src/t8_forest/t8_forest_ghost.cxx +++ b/src/t8_forest/t8_forest_ghost.cxx @@ -152,7 +152,7 @@ t8_ghost_remote_equal_function (const void *remote_dataa, const void *remote_dat } /** This struct is used during a ghost data exchange. - * Since we use asynchronuous communication, we store the + * Since we use asynchronous communication, we store the * send buffers and mpi requests until we end the communication. */ typedef struct @@ -682,7 +682,7 @@ t8_forest_ghost_fill_remote (t8_forest_t forest, t8_forest_ghost_t ghost, int gh t8_locidx_t num_local_trees, num_tree_elems; t8_locidx_t itree, ielem; t8_tree_t tree; - t8_eclass_t neigh_class, last_class; + t8_eclass_t last_class; t8_gloidx_t neighbor_tree; int iface, num_faces; @@ -726,7 +726,7 @@ t8_forest_ghost_fill_remote (t8_forest_t forest, t8_forest_ghost_t ghost, int gh * Currently we perform this check in the half_neighbors function. */ /* Get the element class of the neighbor tree */ - neigh_class = t8_forest_element_neighbor_eclass (forest, itree, elem, iface); + const t8_eclass_t neigh_class = t8_forest_element_neighbor_eclass (forest, itree, elem, iface); if (ghost_method == 0) { /* Use half neighbors */ /* Get the number of face children of the element at this face */ @@ -735,11 +735,6 @@ t8_forest_ghost_fill_remote (t8_forest_t forest, t8_forest_ghost_t ghost, int gh * We also need to reallocate it, if the element class of the neighbor * changes */ if (max_num_face_children < num_face_children || last_class != neigh_class) { - if (max_num_face_children > 0) { - /* Clean-up memory */ - scheme->element_destroy (last_class, max_num_face_children, half_neighbors); - T8_FREE (half_neighbors); - } half_neighbors = T8_ALLOC (t8_element_t *, num_face_children); /* Allocate memory for the half size face neighbors */ scheme->element_new (neigh_class, num_face_children, half_neighbors); @@ -770,6 +765,8 @@ t8_forest_ghost_fill_remote (t8_forest_t forest, t8_forest_ghost_t ghost, int gh } } } + scheme->element_destroy (neigh_class, num_face_children, half_neighbors); + T8_FREE (half_neighbors); } /* end ghost_method 0 */ else { size_t iowner; @@ -797,13 +794,7 @@ t8_forest_ghost_fill_remote (t8_forest_t forest, t8_forest_ghost_t ghost, int gh forest->profile->ghosts_remotes = ghost->remote_processes->elem_count; } /* Clean-up memory */ - if (ghost_method == 0) { - if (half_neighbors != NULL) { - scheme->element_destroy (neigh_class, max_num_face_children, half_neighbors); - T8_FREE (half_neighbors); - } - } - else { + if (ghost_method != 0) { sc_array_reset (&owners); sc_array_reset (&tree_owners); } diff --git a/src/t8_forest/t8_forest_iterate.cxx b/src/t8_forest/t8_forest_iterate.cxx index 87cdd2dad1..3b381b1b7f 100644 --- a/src/t8_forest/t8_forest_iterate.cxx +++ b/src/t8_forest/t8_forest_iterate.cxx @@ -536,4 +536,11 @@ t8_forest_iterate_replace (t8_forest_t forest_new, t8_forest_t forest_old, t8_fo t8_global_productionf ("Done t8_forest_iterate_replace\n"); } +void +t8_forest_search_partition (const t8_forest_t forest, t8_forest_partition_search_fn search_fn, + t8_forest_partition_query_fn query_fn, sc_array_t *queries) +{ + SC_ABORT ("not implemented yet"); +} + T8_EXTERN_C_END (); diff --git a/src/t8_forest/t8_forest_iterate.h b/src/t8_forest/t8_forest_iterate.h index 5f1ac2f27b..06eebf1f7e 100644 --- a/src/t8_forest/t8_forest_iterate.h +++ b/src/t8_forest/t8_forest_iterate.h @@ -79,6 +79,48 @@ typedef void (*t8_forest_query_fn) (t8_forest_t forest, const t8_locidx_t ltreei const int is_leaf, const t8_element_array_t *leaf_elements, const t8_locidx_t tree_leaf_index, sc_array_t *queries, sc_array_t *query_indices, int *query_matches, const size_t num_active_queries); +/** + * A call-back function used by \ref t8_forest_search_partition describing a search-criterion. Is called on an element + * and the search criterion should be checked on that element. Return true if the search criterion is met, false + * otherwise. + * + * \param[in] forest the forest + * \param[in] ltreeid the local tree id of the current tree in the cmesh. Since the cmesh has to be + * replicated, it coincides with the global tree id. + * \param[in] element the element for which the search criterion is checked + * \param[in] pfirst the first processor that owns part of \a element. Guaranteed to be non-empty. + * \param[in] plast the last processor that owns part of \a element. Guaranteed to be non-empty. + * \returns non-zero if the search criterion is met, zero otherwise. + */ +typedef int (*t8_forest_partition_search_fn) (const t8_forest_t forest, const t8_locidx_t ltreeid, + const t8_element_t *element, const int pfirst, const int plast); + +/** + * A call-back function used by \ref t8_forest_search_partition for queries. Is called on an element and all queries are + * checked on that element. All positive queries are passed further down to the children of the element. The results of + * the check are stored in \a query_matches. + * + * \param[in] forest the forest + * \param[in] ltreeid the local tree id of the current tree in the cmesh. Since the cmesh has to be + * replicated, it coincides with the global tree id. + * \param[in] element the element for which the query is executed + * \param[in] pfirst the first processor that owns part of \a element. Guaranteed to be non-empty. + * \param[in] plast the last processor that owns part of \a element. Guaranteed to be non-empty. + * if this is equal to \a pfirst, then the recursion will stop for + * \a element's branch after this function returns. + * \param[in] queries an array of queries that are checked by the function + * \param[in] query_indices an array of size_t entries, where each entry is an index of a query in \a queries. + * \param[in, out] query_matches an array of length \a num_active_queries. + * If the element is not a leaf must be set to true or false at the i-th index for + * each query, specifying whether the element 'matches' the query of the i-th query + * index or not. When the element is a leaf we can return before all entries are set. + * \param[in] num_active_queries The number of currently active queries (equals the number of entries of + * \a query_matches and entries of \a query_indices). + */ +typedef void (*t8_forest_partition_query_fn) (const t8_forest_t forest, const t8_locidx_t ltreeid, + const t8_element_t *element, const int pfirst, const int plast, + void *queries, sc_array_t *query_indices, int *query_matches, + const size_t num_active_queries); T8_EXTERN_C_BEGIN (); @@ -134,6 +176,25 @@ t8_forest_search (t8_forest_t forest, t8_forest_search_fn search_fn, t8_forest_q void t8_forest_iterate_replace (t8_forest_t forest_new, t8_forest_t forest_old, t8_forest_replace_t replace_fn); +/** + * Perform a top-down search of the global partition, executing a callback on + * each intermediate element. The search will enter each tree at least once. + * The recursion will only go down branches that are split between multiple processors. + * This is not a collective function. It does not communicate. + * The function expects the coarse mesh to be replicated. + * If the callback returns false for an element, its descendants + * are not further searched. + * To pass user data to \b search_fn function use \ref t8_forest_set_user_data + * + * \param[in] forest the forest to be searched + * \param[in] search_fn a search callback function called on elements + * \param[in] query_fn a query callback function called for all active queries of an element + * \param[in,out] queries an array of queries that are checked by the function + */ +void +t8_forest_search_partition (const t8_forest_t forest, t8_forest_partition_search_fn search_fn, + t8_forest_partition_query_fn query_fn, sc_array_t *queries); + T8_EXTERN_C_END (); #endif /* !T8_FOREST_ITERATE_H */ diff --git a/src/t8_forest/t8_forest_search/t8_forest_search.cxx b/src/t8_forest/t8_forest_search/t8_forest_search.cxx new file mode 100644 index 0000000000..82842763fc --- /dev/null +++ b/src/t8_forest/t8_forest_search/t8_forest_search.cxx @@ -0,0 +1,353 @@ +/* +This file is part of t8code. +t8code is a C library to manage a collection (a forest) of multiple +connected adaptive space-trees of general element classes in parallel. + +Copyright (C) 2024 the developers + +t8code 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. + +t8code 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 t8code; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "t8_forest/t8_forest_search/t8_forest_search.hxx" +#include "t8_forest/t8_forest_search/t8_forest_search.h" +#include +#include +#include +#include + +void +t8_search_base::search_recursion (const t8_locidx_t ltreeid, t8_element_t *element, const t8_scheme *ts, + t8_element_array_t *leaf_elements, const t8_locidx_t tree_lindex_of_first_leaf) +{ + /* Assertions to check for necessary requirements */ + /* The forest must be committed */ + T8_ASSERT (t8_forest_is_committed (this->forest)); + /* The tree must be local */ + T8_ASSERT (0 <= ltreeid && ltreeid < t8_forest_get_num_local_trees (this->forest)); + + const size_t elem_count = t8_element_array_get_count (leaf_elements); + if (elem_count == 0) { + /* There are no leaves left, so we have nothing to do */ + return; + } + + if (this->stop_due_to_queries ()) { + return; + } + + const t8_eclass_t eclass = t8_forest_get_eclass (this->forest, ltreeid); + + bool is_leaf = false; + if (elem_count == 1) { + /* There is only one leaf left, we check whether it is the same as element and if so call the callback function */ + const t8_element_t *leaf = t8_element_array_index_locidx (leaf_elements, 0); + + SC_CHECK_ABORT (ts->element_get_level (eclass, element) <= ts->element_get_level (eclass, leaf), + "Search: element level greater than leaf level\n"); + if (ts->element_get_level (eclass, element) == ts->element_get_level (eclass, leaf)) { + T8_ASSERT (t8_forest_element_is_leaf (this->forest, leaf, ltreeid)); + T8_ASSERT (ts->element_is_equal (eclass, element, leaf)); + /* The element is the leaf */ + is_leaf = true; + } + } + /* Call the callback function for the element */ + const bool ret = check_element (ltreeid, element, is_leaf, leaf_elements, tree_lindex_of_first_leaf); + + if (!ret) { + /* The function returned false. We abort the recursion */ + return; + } + std::vector new_active_queries; + this->check_queries (new_active_queries, ltreeid, element, is_leaf, leaf_elements, tree_lindex_of_first_leaf); + + if (is_leaf) { + return; + } + + /* Enter the recursion (the element is definitely not a leaf at this point) */ + /* We compute all children of E, compute their leaf arrays and call search_recursion */ + /* allocate the memory to store the children */ + const int num_children = ts->element_get_num_children (eclass, element); + t8_element_t **children = T8_ALLOC (t8_element_t *, num_children); + ts->element_new (eclass, num_children, children); + /* Memory for the indices that split the leaf_elements array */ + size_t *split_offsets = T8_ALLOC (size_t, num_children + 1); + /* Compute the children */ + ts->element_get_children (eclass, element, num_children, children); + /* Split the leaves array in portions belonging to the children of element */ + t8_forest_split_array (element, leaf_elements, split_offsets); + for (int ichild = 0; ichild < num_children; ichild++) { + /* Check if there are any leaf elements for this child */ + const size_t indexa = split_offsets[ichild]; /* first leaf of this child */ + const size_t indexb = split_offsets[ichild + 1]; /* first leaf of next child */ + if (indexa < indexb) { + t8_element_array_t child_leaves; + /* There exist leaves of this child in leaf_elements, + * we construct an array of these leaves */ + t8_element_array_init_view (&child_leaves, leaf_elements, indexa, indexb - indexa); + /* Enter the recursion */ + search_recursion (ltreeid, children[ichild], ts, &child_leaves, indexa + tree_lindex_of_first_leaf); + update_queries (new_active_queries); + } + } + + /* clean-up */ + ts->element_destroy (eclass, num_children, children); + T8_FREE (children); + T8_FREE (split_offsets); +} + +void +t8_search_base::search_tree (const t8_locidx_t ltreeid) +{ + const t8_eclass_t eclass = t8_forest_get_eclass (this->forest, ltreeid); + const t8_scheme *ts = t8_forest_get_scheme (this->forest); + t8_element_array_t *leaf_elements = t8_forest_tree_get_leaves (this->forest, ltreeid); + + /* assert for empty tree */ + T8_ASSERT (t8_element_array_get_count (leaf_elements) >= 0); + /* Get the first and last leaf of this tree */ + const t8_element_t *first_el = t8_element_array_index_locidx (leaf_elements, 0); + const t8_element_t *last_el + = t8_element_array_index_locidx (leaf_elements, t8_element_array_get_count (leaf_elements) - 1); + /* Compute their nearest common ancestor */ + t8_element_t *nca; + ts->element_new (eclass, 1, &nca); + ts->element_get_nca (eclass, first_el, last_el, nca); + + /* Start the top-down search */ + this->search_recursion (ltreeid, nca, ts, leaf_elements, 0); + + ts->element_destroy (eclass, 1, &nca); +} + +void +t8_search_base::do_search () +{ + T8_ASSERT (t8_forest_is_committed (forest)); + const t8_locidx_t num_local_trees = t8_forest_get_num_local_trees (this->forest); + for (t8_locidx_t itree = 0; itree < num_local_trees; itree++) { + this->search_tree (itree); + } +} + +/* #################### t8_forest_search c interface #################### */ +T8_EXTERN_C_BEGIN (); + +struct t8_forest_c_search +{ + t8_search *cpp_search; +}; + +void +t8_forest_init_search (t8_forest_search_c_wrapper search, t8_search_element_callback_c_wrapper element_callback, + const t8_forest_t forest) +{ + T8_ASSERT (search != NULL); + T8_ASSERT (element_callback != NULL); + search->cpp_search = new t8_search (element_callback, forest); +} + +void +t8_forest_search_update_forest (t8_forest_search_c_wrapper search, const t8_forest_t forest) +{ + T8_ASSERT (search != NULL); + T8_ASSERT (forest != NULL); + search->cpp_search->update_forest (forest); +} + +void +t8_forest_search_update_user_data (t8_forest_search_c_wrapper search, void *udata) +{ + T8_ASSERT (search != NULL); + T8_ASSERT (udata != NULL); + search->cpp_search->update_user_data (&udata); +} + +void +t8_forest_search_do_search (t8_forest_search_c_wrapper search) +{ + T8_ASSERT (search != NULL); + search->cpp_search->do_search (); +} + +void +t8_forest_search_destroy (t8_forest_search_c_wrapper search) +{ + T8_ASSERT (search != NULL); + delete search->cpp_search; + search->cpp_search = NULL; +} + +struct t8_forest_search_with_queries +{ + t8_search_with_queries *cpp_search; +}; + +void +t8_forest_init_search_with_queries (t8_forest_search_with_queries_c_wrapper search_with_queries, + t8_search_element_callback_c_wrapper element_callback, + t8_search_queries_callback_c_wrapper queries_callback, void **queries, + const size_t num_queries, const t8_forest_t forest) +{ + T8_ASSERT (search_with_queries != NULL); + T8_ASSERT (element_callback != NULL); + T8_ASSERT (queries_callback != NULL); + T8_ASSERT (queries != NULL); + T8_ASSERT (forest != NULL); + + std::vector queries_vector = std::vector (queries, queries + num_queries); + + search_with_queries->cpp_search + = new t8_search_with_queries (element_callback, queries_callback, queries_vector, forest); +} + +void +t8_forest_search_with_queries_update_forest (t8_forest_search_with_queries_c_wrapper search_with_queries, + const t8_forest_t forest) +{ + T8_ASSERT (search_with_queries != NULL); + T8_ASSERT (forest != NULL); + search_with_queries->cpp_search->update_forest (forest); +} + +void +t8_forest_search_with_queries_update_user_data (t8_forest_search_with_queries_c_wrapper search_with_queries, + void *udata) +{ + T8_ASSERT (search_with_queries != NULL); + T8_ASSERT (udata != NULL); + search_with_queries->cpp_search->update_user_data (&udata); +} + +void +t8_forest_search_with_queries_update_queries (t8_forest_search_with_queries_c_wrapper search_with_queries, + void **queries, const size_t num_queries) +{ + T8_ASSERT (search_with_queries != NULL); + T8_ASSERT (queries != NULL); + + std::vector queries_vector = std::vector (queries, queries + num_queries); + + search_with_queries->cpp_search->update_queries (queries_vector); +} + +void +t8_forest_search_with_queries_do_search (t8_forest_search_with_queries_c_wrapper search) +{ + T8_ASSERT (search != NULL); + search->cpp_search->do_search (); +} + +void +t8_forest_search_with_queries_destroy (t8_forest_search_with_queries_c_wrapper search) +{ + T8_ASSERT (search != NULL); + delete search->cpp_search; + search->cpp_search = NULL; +} + +struct t8_forest_search_with_batched_queries +{ + t8_search_with_batched_queries *cpp_search; + t8_search_batched_queries_callback_c_wrapper queries_callback; + + void + wrapped_queries_callback (const t8_forest_t forest, const t8_locidx_t ltreeid, const t8_element_t *element, + const bool is_leaf, const t8_element_array_t *leaf_elements, + const t8_locidx_t tree_leaf_index, const std::vector &queries, + const std::vector &active_query_indices, std::vector &query_matches, + void *user_data) + { + std::vector query_matches_int (query_matches.size ()); + queries_callback (forest, ltreeid, element, is_leaf, leaf_elements, tree_leaf_index, queries.data (), + active_query_indices.data (), query_matches_int.data (), user_data); + std::transform (query_matches_int.begin (), query_matches_int.end (), query_matches.begin (), + [] (int val) { return static_cast (val); }); + } +}; + +void +t8_forest_init_search_with_batched_queries (t8_forest_search_with_batched_queries_c_wrapper search_with_queries, + t8_search_element_callback_c_wrapper element_callback, + t8_search_batched_queries_callback_c_wrapper queries_callback, + void **queries, const size_t num_queries, const t8_forest_t forest) +{ + T8_ASSERT (search_with_queries != NULL); + T8_ASSERT (element_callback != NULL); + T8_ASSERT (queries_callback != NULL); + T8_ASSERT (queries != NULL); + T8_ASSERT (forest != NULL); + + std::vector queries_vector = std::vector (queries, queries + num_queries); + + search_with_queries->queries_callback = queries_callback; + + search_with_queries->cpp_search = new t8_search_with_batched_queries ( + element_callback, + [&search_with_queries] ( + const t8_forest_t forest, const t8_locidx_t ltreeid, const t8_element_t *element, const bool is_leaf, + const t8_element_array_t *leaf_elements, const t8_locidx_t tree_leaf_index, const std::vector &queries, + const std::vector &active_query_indices, std::vector &query_matches, void *user_data) { + search_with_queries->wrapped_queries_callback (forest, ltreeid, element, is_leaf, leaf_elements, tree_leaf_index, + queries, active_query_indices, query_matches, user_data); + }, + queries_vector, forest); +} + +void +t8_forest_search_with_batched_queries_update_forest ( + t8_forest_search_with_batched_queries_c_wrapper search_with_queries, const t8_forest_t forest) +{ + T8_ASSERT (search_with_queries != NULL); + T8_ASSERT (forest != NULL); + search_with_queries->cpp_search->update_forest (forest); +} + +void +t8_forest_search_with_batched_queries_update_user_data ( + t8_forest_search_with_batched_queries_c_wrapper search_with_queries, void *udata) +{ + T8_ASSERT (search_with_queries != NULL); + T8_ASSERT (udata != NULL); + search_with_queries->cpp_search->update_user_data (&udata); +} +void +t8_forest_search_with_batched_queries_update_queries ( + t8_forest_search_with_batched_queries_c_wrapper search_with_queries, void **queries, const size_t num_queries) +{ + T8_ASSERT (search_with_queries != NULL); + T8_ASSERT (queries != NULL); + + std::vector queries_vector = std::vector (queries, queries + num_queries); + + search_with_queries->cpp_search->update_queries (queries_vector); +} +void +t8_forest_search_with_batched_queries_destroy (t8_forest_search_with_batched_queries_c_wrapper search) +{ + T8_ASSERT (search != NULL); + delete search->cpp_search; + search->cpp_search = NULL; +} + +void +t8_forest_search_with_batched_queries_do_search (t8_forest_search_with_batched_queries_c_wrapper search) +{ + T8_ASSERT (search != NULL); + search->cpp_search->do_search (); +} +T8_EXTERN_C_END (); diff --git a/src/t8_forest/t8_forest_search/t8_forest_search.h b/src/t8_forest/t8_forest_search/t8_forest_search.h new file mode 100644 index 0000000000..6991f3dc76 --- /dev/null +++ b/src/t8_forest/t8_forest_search/t8_forest_search.h @@ -0,0 +1,110 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2024 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_forest_search.h + * This is the C interface for the search functionality. The user can define search and query callbacks + * to perform the search and query operations on the forest. +*/ + +#ifndef T8_FOREST_SEARCH_C_INTERFACE_H +#define T8_FOREST_SEARCH_C_INTERFACE_H + +#include + +T8_EXTERN_C_BEGIN (); + +typedef int (*t8_search_element_callback_c_wrapper) (t8_forest_t forest, const t8_locidx_t ltreeid, + const t8_element_t *element, const int is_leaf, + const t8_element_array_t *leaf_elements, + const t8_locidx_t tree_leaf_index, void *user_data); + +typedef int (*t8_search_queries_callback_c_wrapper) (t8_forest_t forest, const t8_locidx_t ltreeid, + const t8_element_t *element, const int is_leaf, + const t8_element_array_t *leaf_elements, + const t8_locidx_t tree_leaf_index, void *queries, void *user_data); + +typedef void (*t8_search_batched_queries_callback_c_wrapper) (t8_forest_t forest, const t8_locidx_t ltreeid, + const t8_element_t *element, const int is_leaf, + const t8_element_array_t *leaf_elements, + const t8_locidx_t tree_leaf_index, const void *queries, + const size_t *active_query_indices, int *query_matches, + void *user_data); + +typedef struct t8_forest_c_search *t8_forest_search_c_wrapper; + +void +t8_forest_init_search (t8_forest_search_c_wrapper search, t8_search_element_callback_c_wrapper element_callback, + const t8_forest_t forest); +void +t8_forest_search_update_forest (t8_forest_search_c_wrapper search, const t8_forest_t forest); +void +t8_forest_search_update_user_data (t8_forest_search_c_wrapper search, void *udata); +void +t8_forest_search_do_search (t8_forest_search_c_wrapper search); +void +t8_forest_search_destroy (t8_forest_search_c_wrapper search); + +typedef struct t8_forest_search_with_queries *t8_forest_search_with_queries_c_wrapper; + +void +t8_forest_init_search_with_queries (t8_forest_search_with_queries_c_wrapper search_with_queries, + t8_search_element_callback_c_wrapper element_callback, + t8_search_queries_callback_c_wrapper queries_callback, void **queries, + const size_t num_queries, const t8_forest_t forest); +void +t8_forest_search_with_queries_update_forest (t8_forest_search_with_queries_c_wrapper search_with_queries, + const t8_forest_t forest); +void +t8_forest_search_with_queries_update_user_data (t8_forest_search_with_queries_c_wrapper search_with_queries, + void *udata); +void +t8_forest_search_with_queries_update_queries (t8_forest_search_with_queries_c_wrapper search_with_queries, + void **queries, const size_t num_queries); +void +t8_forest_search_with_queries_destroy (t8_forest_search_with_queries_c_wrapper search); +void +t8_forest_search_with_queries_do_search (t8_forest_search_with_queries_c_wrapper search); + +typedef struct t8_forest_search_with_batched_queries *t8_forest_search_with_batched_queries_c_wrapper; + +void +t8_forest_init_search_with_batched_queries (t8_forest_search_with_batched_queries_c_wrapper search_with_queries, + t8_search_element_callback_c_wrapper element_callback, + t8_search_batched_queries_callback_c_wrapper queries_callback, + void **queries, const size_t num_queries, const t8_forest_t forest); +void +t8_forest_search_with_batched_queries_update_forest ( + t8_forest_search_with_batched_queries_c_wrapper search_with_queries, const t8_forest_t forest); +void +t8_forest_search_with_batched_queries_update_user_data ( + t8_forest_search_with_batched_queries_c_wrapper search_with_queries, void *udata); +void +t8_forest_search_with_batched_queries_update_queries ( + t8_forest_search_with_batched_queries_c_wrapper search_with_queries, void **queries, const size_t num_queries); +void +t8_forest_search_with_batched_queries_destroy (t8_forest_search_with_batched_queries_c_wrapper search); +void +t8_forest_search_with_batched_queries_do_search (t8_forest_search_with_batched_queries_c_wrapper search); + +T8_EXTERN_C_END (); + +#endif // T8_FOREST_SEARCH_C_INTERFACE_H diff --git a/src/t8_forest/t8_forest_search/t8_forest_search.hxx b/src/t8_forest/t8_forest_search/t8_forest_search.hxx new file mode 100644 index 0000000000..5ee577180e --- /dev/null +++ b/src/t8_forest/t8_forest_search/t8_forest_search.hxx @@ -0,0 +1,816 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2024 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_forest_search.hxx + * A C++ interface for the search functionality. The user can define search and query callbacks + * to perform the search and query operations on the forest. Implementation details regarding the + * callback handling are given by https://stackoverflow.com/questions/2298242/callback-functions-in-c + * We decided for option 4, using std::function together with templates. +*/ + +#ifndef T8_FOREST_SEARCH_HXX +#define T8_FOREST_SEARCH_HXX + +#include +#include +#include // Ensure t8_forest_t is defined +#include +#include +#include + +/** + * \typedef t8_search_element_callback + * \brief A callback function type used for searching elements in a forest. + * + * This callback function is invoked during the search process in a forest. It allows + * custom operations to be performed on each element encountered during the search. + * + * \tparam Udata The type of user data passed to the callback. Defaults to void. + * + * \param[in] forest The forest in which the search is being performed. + * \param[in] ltreeid The local tree ID of the current element. + * \param[in] element A pointer to the current element being processed. + * \param[in] is_leaf A bool indicating whether the current element is a leaf (non-zero) or not (zero). + * \param[in] leaf_elements A pointer to an array of leaf elements. + * \param[in] tree_leaf_index The index of the current leaf element within the tree. + * \param[in] user_data A reference to user-defined data passed to the callback. + * + * \return True if the search should continue, false otherwise. + */ +template +using t8_search_element_callback = std::function; + +/** + * \typedef t8_search_queries_callback + * \brief A callback function type used for search queries within a forest. + * + * \tparam Query_T The type of the query. + * \tparam Udata The type of user data, defaults to void. + * + * \param[in] forest The forest in which the search is performed. + * \param[in] ltreeid The local tree ID within the forest. + * \param[in] element The element being queried. + * \param[in] is_leaf A flag indicating if the element is a leaf. + * \param[in] leaf_elements The array of leaf elements. + * \param[in] tree_leaf_index The index of the leaf within the tree. + * \param[in] query A single query to be processed. + * \param[in] user_data User-defined data passed to the callback. + */ +template +using t8_search_query_callback = std::function; + +/** + * \typedef t8_search_batched_queries_callback + * \brief A callback function type used for search queries within a forest. Processes a batch of queries. + * + * \tparam Query_T The type of the query. + * \tparam Udata The type of user data, defaults to void. + * + * \param[in] forest The forest in which the search is performed. + * \param[in] ltreeid The local tree ID within the forest. + * \param[in] element The element being queried. + * \param[in] is_leaf A flag indicating if the element is a leaf. + * \param[in] leaf_elements The array of leaf elements. + * \param[in] tree_leaf_index The index of the leaf within the tree. + * \param[in] queries A vector of queries to be processed. + * \param[in, out] active_query_indices A vector of indices of active queries. + * \param[in, out] query_matches A vector of query matches. Each entry corresponds to a query in the queries vector. + * \param[in] user_data User-defined data passed to the callback. + */ +template +using t8_search_batched_queries_callback = std::function &queries, + const std::vector &active_query_indices, std::vector &query_matches, Udata *user_data)>; + +/** + * \typedef t8_partition_search_element_callback + * \brief A callback function type used for searching elements in the partition of a forest. + * + * This callback function is invoked during the partition search process in a forest. It allows + * custom operations to be performed on each element encountered during the search. + * + * \tparam Udata The type of user data passed to the callback. Defaults to void. + * + * \param[in] forest The forest whose partition is searched. + * \param[in] ltreeid The local tree ID of the current tree in the cmesh. + * \param[in] element A pointer to the current element being processed. + * \param[in] pfirst The first processor that owns part of \a element. Guaranteed to be non-empty. + * \param[in] plast The last processor that owns part of \a element. Guaranteed to be non-empty. + * + * \return True, if the search should continue, false otherwise. + */ +template +using t8_partition_search_element_callback + = std::function; + +/** + * \typedef t8_partition_search_query_callback + * \brief A callback function type used for searching queries in the partition of a forest. + * + * \tparam Query_T The type of the query. + * \tparam Udata The type of user data, defaults to void. + * + * \param[in] forest The forest whose partition is searched. + * \param[in] ltreeid The local tree ID within the forest. + * \param[in] element The element being queried. + * \param[in] pfirst The first processor that owns part of \a element. Guaranteed to be non-empty. + * \param[in] plast The last processor that owns part of \a element. Guaranteed to be non-empty. + * \param[in] query A single query to be processed. + * \param[in] user_data User-defined data passed to the callback. + */ +template +using t8_partition_search_query_callback + = std::function; + +/** + * \typedef t8_partition_search_batched_queries_callback + * \brief A callback function type used for searching queries in the partition of a forest. Processes a batch of queries. + * + * \tparam Query_T The type of the query. + * \tparam Udata The type of user data, defaults to void. + * + * \param[in] forest The forest whose partition is searched. + * \param[in] ltreeid The local tree ID within the forest. + * \param[in] element The element being queried. + * \param[in] pfirst The first processor that owns part of \a element. Guaranteed to be non-empty. + * \param[in] plast The last processor that owns part of \a element. Guaranteed to be non-empty. + * \param[in] queries A vector of queries to be processed. + * \param[in, out] active_query_indices A vector of indices of active queries. + * \param[in, out] query_matches A vector of query matches. Each entry corresponds to a query in the queries vector. + * \param[in] user_data User-defined data passed to the callback. + */ +template +using t8_partition_search_batched_queries_callback = std::function &queries, const std::vector &active_query_indices, + std::vector &query_matches, Udata *user_data)>; + +class t8_search_base { + public: + /** \brief Constructor for the t8_search_base class. + * + * + * This constructor initializes a t8_search_base object with the given forest. + * If the forest is not null, it increments the reference count of the forest + * and asserts that the forest is committed. + * + * \param[in] forest A pointer to a t8_forest_t object. Defaults to nullptr. + */ + t8_search_base (t8_forest_t forest = nullptr): forest (forest) + { + if (forest != nullptr) { + t8_forest_ref (forest); + T8_ASSERT (t8_forest_is_committed (forest)); + } + } + + /** \brief Update the forest for the search. + * + * This function updates the forest for the search. If the current forest is not null, + * it decrements the reference count of the forest. It then asserts that the new forest + * is not null and is committed. Finally, it increments the reference count of the new forest. + * + * \param[in] forest A pointer to a t8_forest_t object. + */ + void + update_forest (t8_forest_t forest) + { + if (this->forest != nullptr) { + t8_forest_unref (&(this->forest)); + } + T8_ASSERT (forest != nullptr); + T8_ASSERT (t8_forest_is_committed (forest)); + t8_forest_ref (forest); + this->forest = forest; + } + + /** \brief Destructor for the t8_search_base class. + * + * This destructor decrements the reference count of the forest if it is not null. + */ + ~t8_search_base () + { + if (this->forest != nullptr) { + t8_forest_unref (&(this->forest)); + } + } + + /** \brief Perform the search. + * + * This function performs the search in the forest. + */ + void + do_search (); + + t8_forest_t forest; + + private: + /** @brief Searches a tree within the forest. + * + * This function performs a search operation on a tree identified by the given local tree ID. + * It uses the \a search_recursion function to perform the search. + * + * \param[in] ltreeid The local tree ID of the tree to be searched. + */ + void + search_tree (const t8_locidx_t ltreeid); + + /** \brief Recursively searches the tree. + * + * This function performs a recursive search operation on the tree identified by the given local tree ID. + * It uses the given \a element_callback function to process each element encountered during the search. + * If a query_callback function is provided, it is used to process queries during the search. + * + * \param[in] ltreeid The local tree ID of the tree to be searched. + * \param[in] element The element to be searched. + * \param[in] ts The element class scheme. + * \param[in] leaf_elements The array of leaf elements. + * \param[in] tree_lindex_of_first_leaf The index of the first leaf in the tree. + */ + void + search_recursion (const t8_locidx_t ltreeid, t8_element_t *element, const t8_scheme *ts, + t8_element_array_t *leaf_elements, const t8_locidx_t tree_lindex_of_first_leaf); + + /** \brief Checks if the search should stop due to empty queries. + * + */ + virtual bool + stop_due_to_queries () + = 0; + + /** \brief Checks an element during the search. + * + * This function is called for each element encountered during the search. + * It passes the arguments to the callback function provided by the user. + * + * \param[in] ltreeid The local tree ID of the current element. + * \param[in] element A pointer to the current element being processed. + * \param[in] is_leaf A bool indicating whether the current element is a leaf (non-zero) or not (zero). + * \param[in] leaf_elements A pointer to an array of leaf elements. + * \param[in] tree_leaf_index The index of the current leaf element within the tree. + * + * \return True if the search should continue, false otherwise. + */ + virtual bool + check_element (const t8_locidx_t ltreeid, const t8_element_t *element, const bool is_leaf, + const t8_element_array_t *leaf_elements, const t8_locidx_t tree_leaf_index) + = 0; + + /** \brief Checks queries during the search. + * + * This function is called to check queries during the search. + * It passes the arguments to the callback function provided by the user. + * + * \param[in] new_active_queries A vector of indices of active queries. + * \param[in] ltreeid The local tree ID of the current element. + * \param[in] element A pointer to the current element being processed. + * \param[in] is_leaf A bool indicating whether the current element is a leaf (non-zero) or not (zero). + * \param[in] leaf_elements A pointer to an array of leaf elements. + * \param[in] tree_leaf_index The index of the current leaf element within the tree. + */ + virtual void + check_queries (std::vector &new_active_queries, const t8_locidx_t ltreeid, const t8_element_t *element, + const bool is_leaf, const t8_element_array_t *leaf_elements, const t8_locidx_t tree_leaf_index) + = 0; + + /** + * \brief Function the gives the user the opportunity to update the queries after + * each step in the recursion. + * + * \param old_query_indices + */ + virtual void + update_queries (std::vector &old_query_indices) + = 0; +}; + +template +class t8_search: public t8_search_base { + public: + t8_search (t8_search_element_callback element_callback, t8_forest_t forest = nullptr, + Udata *user_data = nullptr) + : t8_search_base (forest), element_callback (element_callback) + { + if (user_data != nullptr) { + this->user_data = user_data; + } + } + + /** \brief Updates the user data associated with the object. + * + * This function sets the user data pointer to the provided Udata object. + * + * \param[in] udata A pointer to the Udata object to be set as the user data. + */ + void + update_user_data (Udata *udata) + { + if (udata != nullptr) { + this->user_data = udata; + } + } + + virtual ~t8_search () = default; + + Udata *user_data; + + private: + bool + stop_due_to_queries () override + { + return false; + } + + bool + check_element (const t8_locidx_t ltreeid, const t8_element_t *element, const bool is_leaf, + const t8_element_array_t *leaf_elements, const t8_locidx_t tree_leaf_index) override + { + T8_ASSERT (t8_forest_is_committed (this->forest)); + return this->element_callback (this->forest, ltreeid, element, is_leaf, leaf_elements, tree_leaf_index, user_data); + } + + void + check_queries (std::vector &new_active_queries, const t8_locidx_t ltreeid, const t8_element_t *element, + const bool is_leaf, const t8_element_array_t *leaf_elements, + const t8_locidx_t tree_leaf_index) override + { + return; + } + + void + update_queries (std::vector &old_query_indices) + { + return; + } + + t8_search_element_callback element_callback; +}; + +/** + * \brief A class that performs a search in a forest with queries. + * Uses a filter-view to filter out the active queries. It is recommended to use this version of the search + * if the number of queries is small or if the queries do not need any further computations to be evaluated. + * + * \tparam Query_T The type of queries + * \tparam Udata The type of the user data, defaults to void. +*/ +template +class t8_search_with_queries: public t8_search { + public: + t8_search_with_queries (t8_search_element_callback element_callback, + t8_search_query_callback queries_callback, std::vector &queries, + const t8_forest_t forest = nullptr, Udata *user_data = nullptr) + : t8_search (element_callback, forest, user_data), queries_callback (queries_callback), queries (queries) + { + this->active_queries.resize (queries.size ()); + std::iota (this->active_queries.begin (), this->active_queries.end (), 0); + } + + void + update_queries (std::vector &queries) + { + this->queries = queries; + } + + ~t8_search_with_queries () + { + } + + private: + bool + stop_due_to_queries () override + { + return this->active_queries.empty (); + } + + void + check_queries (std::vector &new_active_queries, const t8_locidx_t ltreeid, const t8_element_t *element, + const bool is_leaf, const t8_element_array_t *leaf_elements, + const t8_locidx_t tree_leaf_index) override + { + T8_ASSERT (new_active_queries.empty ()); + if (!this->active_queries.empty ()) { + auto positive_queries = this->active_queries | std::ranges::views::filter ([&] (size_t &query_index) { + return this->queries_callback (this->forest, ltreeid, element, is_leaf, leaf_elements, + tree_leaf_index, queries[query_index], this->user_data); + }); + if (!is_leaf) { + new_active_queries.assign (positive_queries.begin (), positive_queries.end ()); + std::swap (this->active_queries, new_active_queries); + } + } + } + + void + update_queries (std::vector &old_query_indices) + { + std::swap (this->active_queries, old_query_indices); + } + + t8_search_query_callback queries_callback; + std::vector &queries; + std::vector active_queries; +}; + +/** + * \brief A class that performs a search in a forest with batched queries. + * + * All active queries are passed to the callback function, which processes them in a batch. It is recommended to + * use this version of the searcch if further computations have to be done to evaluate the queries. That way these + * precomputations are not done for every call to the callback again and only have to be evaluated once per call. + * + * \tparam Query_T The type of queries + * \tparam Udata The type of the user data, defaults to void. + */ +template +class t8_search_with_batched_queries: public t8_search { + public: + t8_search_with_batched_queries (t8_search_element_callback element_callback, + t8_search_batched_queries_callback queries_callback, + std::vector &queries, const t8_forest_t forest = nullptr, + Udata *user_data = nullptr) + : t8_search (element_callback, forest, user_data), queries_callback (queries_callback), queries (queries) + { + this->active_queries.resize (queries.size ()); + std::iota (this->active_queries.begin (), this->active_queries.end (), 0); + } + + void + update_queries (std::vector &queries) + { + this->queries = queries; + } + + virtual ~t8_search_with_batched_queries () = default; + + private: + bool + stop_due_to_queries () override + { + return this->active_queries.empty (); + } + void + check_queries (std::vector &new_active_queries, const t8_locidx_t ltreeid, const t8_element_t *element, + const bool is_leaf, const t8_element_array_t *leaf_elements, + const t8_locidx_t tree_leaf_index) override + { + T8_ASSERT (new_active_queries.empty ()); + if (!this->active_queries.empty ()) { + std::vector query_matches (this->active_queries.size ()); + this->queries_callback (this->forest, ltreeid, element, is_leaf, leaf_elements, tree_leaf_index, this->queries, + this->active_queries, query_matches, this->user_data); + if (!is_leaf) { + auto positive_queries = this->active_queries | std::ranges::views::filter ([&] (size_t &query_index) { + return query_matches[query_index]; + }); + new_active_queries.assign (positive_queries.begin (), positive_queries.end ()); + } + } + std::swap (new_active_queries, this->active_queries); + } + + void + update_queries (std::vector &old_query_indices) + { + std::swap (this->active_queries, old_query_indices); + } + + t8_search_batched_queries_callback queries_callback; + std::vector &queries; + std::vector active_queries; +}; + +class t8_partition_search_base { + public: + /** \brief Constructor for the t8_partition_search_base class. + * + * + * This constructor initializes a t8_partition_search_base object with the + * given forest. If the forest is not null, it increments the reference count + * of the forest and asserts that the forest is committed. + * + * \param[in] forest A pointer to a t8_forest_t object. Defaults to nullptr. + */ + t8_partition_search_base (t8_forest_t forest = nullptr): forest (forest) + { + if (forest != nullptr) { + t8_forest_ref (forest); + T8_ASSERT (t8_forest_is_committed (forest)); + } + } + + /** \brief Update the forest for the search. + * + * This function updates the forest for the search. If the current forest is not null, + * it decrements the reference count of the forest. It then asserts that the new forest + * is not null and is committed. Finally, it increments the reference count of the new forest. + * + * \param[in] forest A pointer to a t8_forest_t object. + */ + void + update_forest (t8_forest_t forest) + { + if (this->forest != nullptr) { + t8_forest_unref (&(this->forest)); + } + T8_ASSERT (forest != nullptr); + T8_ASSERT (t8_forest_is_committed (forest)); + t8_forest_ref (forest); + this->forest = forest; + } + + /** \brief Destructor for the t8_partition_search_base class. + * + * This destructor decrements the reference count of the forest if it is not null. + */ + ~t8_partition_search_base () + { + if (this->forest != nullptr) { + t8_forest_unref (&(this->forest)); + } + } + + /** \brief Perform the search. + * + * This function performs the search in the forest. + */ + void + do_search (); + + t8_forest_t forest; + + private: + /** \brief Checks if the search should stop due to empty queries. + * + */ + virtual bool + stop_due_to_queries () + = 0; + + /** \brief Checks an element during the search. + * + * This function is called for each element encountered during the search. + * It passes the arguments to the callback function provided by the user. + * + * \param[in] ltreeid The local tree ID of the current element. + * \param[in] element A pointer to the current element being processed. + * \param[in] is_leaf A bool indicating whether the current element is a leaf (non-zero) or not (zero). + * \param[in] leaf_elements A pointer to an array of leaf elements. + * \param[in] tree_leaf_index The index of the current leaf element within the tree. + * + * \return True if the search should continue, false otherwise. + */ + virtual bool + check_element (const t8_locidx_t ltreeid, const t8_element_t *element, const int pfirst, const int plast) + = 0; + + /** \brief Checks queries during the search. + * + * This function is called to check queries during the search. + * It passes the arguments to the callback function provided by the user. + * + * \param[in] new_active_queries A vector of indices of active queries. + * \param[in] ltreeid The local tree ID of the current element. + * \param[in] element A pointer to the current element being processed. + * \param[in] is_leaf A bool indicating whether the current element is a leaf (non-zero) or not (zero). + * \param[in] leaf_elements A pointer to an array of leaf elements. + * \param[in] tree_leaf_index The index of the current leaf element within the tree. + */ + virtual void + check_queries (std::vector &new_active_queries, const t8_locidx_t ltreeid, const t8_element_t *element, + const int pfirst, const int plast) + = 0; + + /** + * \brief Function the gives the user the opportunity to update the queries after + * each step in the recursion. + * + * \param old_query_indices + */ + virtual void + update_queries (std::vector &old_query_indices) + = 0; +}; + +template +class t8_partition_search: public t8_partition_search_base { + public: + t8_partition_search (t8_partition_search_element_callback element_callback, t8_forest_t forest = nullptr, + Udata *user_data = nullptr) + : t8_partition_search_base (forest), element_callback (element_callback) + { + if (user_data != nullptr) { + this->user_data = user_data; + } + } + + /** \brief Updates the user data associated with the object. + * + * This function sets the user data pointer to the provided Udata object. + * + * \param[in] udata A pointer to the Udata object to be set as the user data. + */ + void + update_user_data (Udata *udata) + { + if (udata != nullptr) { + this->user_data = udata; + } + } + + Udata *user_data; + + private: + bool + stop_due_to_queries () override + { + return false; + } + + bool + check_element (const t8_locidx_t ltreeid, const t8_element_t *element, const int pfirst, const int plast) override + { + T8_ASSERT (t8_forest_is_committed (this->forest)); + return this->element_callback (this->forest, ltreeid, element, pfirst, plast, user_data); + } + + void + check_queries (std::vector &new_active_queries, const t8_locidx_t ltreeid, const t8_element_t *element, + const int pfirst, const int plast) override + { + return; + } + + void + update_queries (std::vector &old_query_indices) + { + return; + } + + t8_partition_search_element_callback element_callback; +}; + +/** + * \brief A class that performs a search in the partition of a forest with queries. + * Uses a filter-view to filter out the active queries. It is recommended to use this version of the search + * if the number of queries is small or if the queries do not need any further computations to be evaluated. + * + * \tparam Query_T The type of queries + * \tparam Udata The type of the user data, defaults to void. +*/ +template +class t8_partition_search_with_queries: public t8_partition_search { + public: + t8_partition_search_with_queries (t8_partition_search_element_callback element_callback, + t8_partition_search_query_callback queries_callback, + std::vector &queries, const t8_forest_t forest = nullptr, + Udata *user_data = nullptr) + : t8_partition_search (element_callback, forest, user_data), queries_callback (queries_callback), + queries (queries) + { + this->active_queries.resize (queries.size ()); + std::iota (this->active_queries.begin (), this->active_queries.end (), 0); + } + + void + update_queries (std::vector &queries) + { + this->queries = queries; + } + + ~t8_partition_search_with_queries () + { + } + + private: + bool + stop_due_to_queries () override + { + return this->active_queries.empty (); + } + + void + check_queries (std::vector &new_active_queries, const t8_locidx_t ltreeid, const t8_element_t *element, + const int pfirst, const int plast) override + { + T8_ASSERT (new_active_queries.empty ()); + if (!this->active_queries.empty ()) { + auto positive_queries = this->active_queries | std::ranges::views::filter ([&] (size_t &query_index) { + return this->queries_callback (this->forest, ltreeid, element, pfirst, plast, + queries[query_index], this->user_data); + }); + if (pfirst != plast) { + new_active_queries.assign (positive_queries.begin (), positive_queries.end ()); + std::swap (this->active_queries, new_active_queries); + } + } + } + + void + update_queries (std::vector &old_query_indices) + { + std::swap (this->active_queries, old_query_indices); + } + + t8_partition_search_query_callback queries_callback; + std::vector &queries; + std::vector active_queries; +}; + +/** + * \brief A class that performs a search in the partition of a forest with batched queries. + * + * All active queries are passed to the callback function, which processes them in a batch. It is recommended to + * use this version of the searcch if further computations have to be done to evaluate the queries. That way these + * precomputations are not done for every call to the callback again and only have to be evaluated once per call. + * + * \tparam Query_T The type of queries + * \tparam Udata The type of the user data, defaults to void. + */ +template +class t8_partition_search_with_batched_queries: public t8_partition_search { + public: + t8_partition_search_with_batched_queries ( + t8_partition_search_element_callback element_callback, + t8_partition_search_batched_queries_callback queries_callback, std::vector &queries, + const t8_forest_t forest = nullptr, Udata *user_data = nullptr) + : t8_partition_search (element_callback, forest, user_data), queries_callback (queries_callback), + queries (queries) + { + this->active_queries.resize (queries.size ()); + std::iota (this->active_queries.begin (), this->active_queries.end (), 0); + } + + void + update_queries (std::vector &queries) + { + this->queries = queries; + } + + ~t8_partition_search_with_batched_queries () + { + } + + private: + bool + stop_due_to_queries () override + { + return this->active_queries.empty (); + } + void + check_queries (std::vector &new_active_queries, const t8_locidx_t ltreeid, const t8_element_t *element, + const int pfirst, const int plast) override + { + T8_ASSERT (new_active_queries.empty ()); + if (!this->active_queries.empty ()) { + std::vector query_matches (this->active_queries.size ()); + this->queries_callback (this->forest, ltreeid, element, pfirst, plast, this->queries, this->active_queries, + query_matches, this->user_data); + if (pfirst != plast) { + auto positive_queries = this->active_queries | std::ranges::views::filter ([&] (size_t &query_index) { + return query_matches[query_index]; + }); + new_active_queries.assign (positive_queries.begin (), positive_queries.end ()); + } + } + std::swap (new_active_queries, this->active_queries); + } + + void + update_queries (std::vector &old_query_indices) + { + std::swap (this->active_queries, old_query_indices); + } + + t8_partition_search_batched_queries_callback queries_callback; + std::vector &queries; + std::vector active_queries; +}; + +#endif // T8_FOREST_SEARCH_HXX diff --git a/src/t8_geometry/t8_geometry_helpers.c b/src/t8_geometry/t8_geometry_helpers.c index 17b3eb3516..4fd3d81b94 100644 --- a/src/t8_geometry/t8_geometry_helpers.c +++ b/src/t8_geometry/t8_geometry_helpers.c @@ -20,7 +20,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include +#include #include #include #include @@ -174,7 +174,7 @@ t8_geom_compute_linear_geometry (t8_eclass_t tree_class, const double *tree_vert /* Get a quad interpolation of the base */ t8_geom_linear_interpolation (base_coords, tree_vertices, T8_ECLASS_MAX_DIM, 2, out_coords + offset_domain_dim); /* Get vector from base to pyramid tip */ - t8_vec_diff (tree_vertices + 4 * T8_ECLASS_MAX_DIM, out_coords + offset_domain_dim, vec); + t8_diff (tree_vertices + 4 * T8_ECLASS_MAX_DIM, out_coords + offset_domain_dim, vec); /* Add vector to base */ for (i_dim = 0; i_dim < 3; i_dim++) { out_coords[offset_domain_dim + i_dim] += vec[i_dim] * ref_coords[offset_tree_dim + 2]; @@ -217,7 +217,7 @@ t8_geom_compute_linear_axis_aligned_geometry (const t8_eclass_t tree_class, cons const int dimension = t8_eclass_to_dimension[tree_class]; /* Compute vector between both points */ double vector[3]; - t8_vec_diff (tree_vertices + T8_ECLASS_MAX_DIM, tree_vertices, vector); + t8_diff (tree_vertices + T8_ECLASS_MAX_DIM, tree_vertices, vector); /* Compute the coordinates of the reference point. */ for (size_t i_coord = 0; i_coord < num_coords; ++i_coord) { @@ -446,7 +446,7 @@ t8_geom_get_tet_face_intersection (const int face, const double *ref_coords, dou /* Calculate the vector from the opposite vertex to the * reference coordinate in reference space */ double vector[3] = { 0 }; - t8_vec_diff (ref_coords, ref_opposite_vertex, vector); + t8_diff (ref_coords, ref_opposite_vertex, vector); /* Calculate t to get the point on the ray (extension of vector), which lies on the face. * The vector will later be multiplied by t to get the exact distance from the opposite vertex to the face intersection. @@ -546,7 +546,7 @@ int t8_vertex_point_inside (const double vertex_coords[3], const double point[3], const double tolerance) { T8_ASSERT (tolerance > 0); - if (t8_vec_dist (vertex_coords, point) > tolerance) { + if (t8_dist (vertex_coords, point) > tolerance) { return 0; } return 1; @@ -558,7 +558,7 @@ t8_line_point_inside (const double *p_0, const double *vec, const double *point, T8_ASSERT (tolerance > 0); double b[3]; /* b = p - p_0 */ - t8_vec_axpyz (p_0, point, b, -1); + t8_axpyz (p_0, point, b, -1); double x = 0; /* Initialized to prevent compiler warning. */ int i; /* So x is the solution to @@ -590,8 +590,8 @@ t8_line_point_inside (const double *p_0, const double *vec, const double *point, * is actually true. */ double vec_check[3] = { vec[0], vec[1], vec[2] }; - t8_vec_ax (vec_check, x); - if (t8_vec_dist (vec_check, b) > tolerance) { + t8_ax (vec_check, x); + if (t8_dist (vec_check, b) > tolerance) { /* Point does not lie on the line. */ return 0; } @@ -617,7 +617,7 @@ t8_triangle_point_inside (const double p_0[3], const double v[3], const double w T8_ASSERT (tolerance > 0); /* negative values and zero are not allowed */ double b[3]; /* b = point - p_0 */ - t8_vec_axpyz (p_0, point, b, -1); + t8_axpyz (p_0, point, b, -1); /* Let d = det (v w e_3) */ const double det_vwe3 = v[0] * w[1] - v[1] * w[0]; @@ -658,9 +658,9 @@ t8_plane_point_inside (const double point_on_face[3], const double face_normal[3 { /* Set x = x - p */ double pof[3] = { point_on_face[0], point_on_face[1], point_on_face[2] }; - t8_vec_axpy (point, pof, -1); + t8_axpy (point, pof, -1); /* Compute */ - const double dot_product = t8_vec_dot (pof, face_normal); + const double dot_product = t8_dot (pof, face_normal); if (dot_product < 0) { /* The point is on the wrong side of the plane */ return 0; diff --git a/src/t8_geometry/t8_geometry_implementations/t8_geometry_examples.cxx b/src/t8_geometry/t8_geometry_implementations/t8_geometry_examples.cxx index 141d1b056f..e42d2decd4 100644 --- a/src/t8_geometry/t8_geometry_implementations/t8_geometry_examples.cxx +++ b/src/t8_geometry/t8_geometry_implementations/t8_geometry_examples.cxx @@ -23,7 +23,7 @@ #include #include #include -#include +#include void t8_geometry_quadrangulated_disk::t8_geom_evaluate (t8_cmesh_t cmesh, t8_gloidx_t gtreeid, const double *ref_coords, @@ -45,14 +45,14 @@ t8_geometry_quadrangulated_disk::t8_geom_evaluate (t8_cmesh_t cmesh, t8_gloidx_t } /* Normal vector along one of the straight edges of the quad. */ - t8_vec_copy (active_tree_vertices, n); - t8_vec_normalize (n); + t8_copy (active_tree_vertices, n); + t8_normalize (n); /* Radial vector parallel to one of the tilted edges of the quad. */ - t8_vec_copy (active_tree_vertices + 9, r); - t8_vec_normalize (r); + t8_copy (active_tree_vertices + 9, r); + t8_normalize (r); - const double inv_denominator = 1.0 / t8_vec_dot (r, n); + const double inv_denominator = 1.0 / t8_dot (r, n); /* Radial reference coordinate index. */ const int r_coord = ((gtreeid - 2) % 3 == 0) ? 0 : 1; @@ -76,18 +76,18 @@ t8_geometry_quadrangulated_disk::t8_geom_evaluate (t8_cmesh_t cmesh, t8_gloidx_t /* Compute and normalize vector `s`. */ t8_geom_linear_interpolation (corr_ref_coords, active_tree_vertices, 3, 2, s); - t8_vec_normalize (s); + t8_normalize (s); } /* Correction in order to rectify elements near the corners. */ t8_geom_linear_interpolation (ref_coords + offset_2d, active_tree_vertices, 3, 2, p); /* Compute intersection of line with a plane. */ - const double out_radius = t8_vec_dot (p, n) * inv_denominator; + const double out_radius = t8_dot (p, n) * inv_denominator; /* Linear blend from flat to curved: `out_coords = (1.0 - r_ref)*p + r_ref_ * out_radius * s`. */ - t8_vec_axy (p, out_coords + offset_3d, 1.0 - r_ref); - t8_vec_axpy (s, out_coords + offset_3d, r_ref * out_radius); + t8_axy (p, out_coords + offset_3d, 1.0 - r_ref); + t8_axpy (s, out_coords + offset_3d, r_ref * out_radius); } } @@ -98,23 +98,23 @@ t8_geom_evaluate_sphere_tri_prism (const double *active_tree_vertices, const t8_ // All elements are aligned such that the reference z-direction follows the // outward radial direction of the sphere. Hence the inner radius is equal to // the norm of the first position vector of `active_tree_vertices`. - const double inner_radius = t8_vec_norm (active_tree_vertices); + const double inner_radius = t8_norm (active_tree_vertices); t8_geom_compute_linear_geometry (eclass, active_tree_vertices, ref_coords, num_coords, out_coords); if (eclass == T8_ECLASS_TRIANGLE) { for (size_t i_coord = 0; i_coord < num_coords; i_coord++) { const size_t offset = 3 * i_coord; - t8_vec_rescale (out_coords + offset, inner_radius); + t8_rescale (out_coords + offset, inner_radius); } } else { const size_t outer_vertex_offset = 3 * 3; - const double shell_thickness = t8_vec_norm (active_tree_vertices + outer_vertex_offset) - inner_radius; + const double shell_thickness = t8_norm (active_tree_vertices + outer_vertex_offset) - inner_radius; for (size_t i_coord = 0; i_coord < num_coords; i_coord++) { const size_t offset = 3 * i_coord; const double z = ref_coords[offset + 2]; - t8_vec_rescale (out_coords + offset, inner_radius + z * shell_thickness); + t8_rescale (out_coords + offset, inner_radius + z * shell_thickness); } } } @@ -149,21 +149,21 @@ t8_geometry_tessellated_spherical_surface::t8_geom_evaluate (t8_cmesh_t cmesh, t double tangent2[3]; // Second tangent vector. // Compute normal vector of the current cmesh cell. - t8_vec_tri_normal (active_tree_vertices, active_tree_vertices + 3, active_tree_vertices + 6, normal); - t8_vec_normalize (normal); + t8_normal_of_tri (active_tree_vertices, active_tree_vertices + 3, active_tree_vertices + 6, normal); + t8_normalize (normal); // Compute sphere's radius over cube root which is the shortest distance to the origin (0,0,0). - const double distance = std::abs (t8_vec_dot (active_tree_vertices, normal)); + const double distance = std::abs (t8_dot (active_tree_vertices, normal)); // Compute actual radius of the sphere. const double radius = distance * std::sqrt (3.0); // Compute orthogonal coordinate system anchored on the cmesh element. - t8_vec_orthogonal_tripod (normal, tangent1, tangent2); + t8_orthogonal_tripod (normal, tangent1, tangent2); // Compute anchor of the tripod on the cmesh element's plane. double anchor[3]; - t8_vec_axy (normal, anchor, distance); + t8_axy (normal, anchor, distance); // Loop over given reference coordinates. for (size_t i_coord = 0; i_coord < num_coords; i_coord++) { @@ -176,19 +176,19 @@ t8_geometry_tessellated_spherical_surface::t8_geom_evaluate (t8_cmesh_t cmesh, t // Compute difference vector between position and tripod's anchor. double diff_vec[3]; - t8_vec_diff (position, anchor, diff_vec); + t8_diff (position, anchor, diff_vec); // Compute the coefficients of the difference vector in the local // coordinate system of the tripod and apply equi-angular correction. - const double alpha1 = distance * tan (0.25 * M_PI * t8_vec_dot (tangent1, diff_vec) / distance); - const double alpha2 = distance * tan (0.25 * M_PI * t8_vec_dot (tangent2, diff_vec) / distance); + const double alpha1 = distance * tan (0.25 * M_PI * t8_dot (tangent1, diff_vec) / distance); + const double alpha2 = distance * tan (0.25 * M_PI * t8_dot (tangent2, diff_vec) / distance); // Compute the final transformed coordinates. double *out_vec = out_coords + offset_3d; - t8_vec_copy (anchor, out_vec); - t8_vec_axpy (tangent1, out_vec, alpha1); - t8_vec_axpy (tangent2, out_vec, alpha2); - t8_vec_rescale (out_vec, radius); + t8_copy (anchor, out_vec); + t8_axpy (tangent1, out_vec, alpha1); + t8_axpy (tangent2, out_vec, alpha2); + t8_rescale (out_vec, radius); } } @@ -205,25 +205,25 @@ t8_geometry_cubed_spherical_shell::t8_geom_evaluate (t8_cmesh_t cmesh, t8_gloidx double tangent2[3]; // Second tangent vector. // Compute normal vector of the current cmesh cell. - t8_vec_tri_normal (active_tree_vertices, active_tree_vertices + 3, active_tree_vertices + 6, normal); - t8_vec_normalize (normal); + t8_normal_of_tri (active_tree_vertices, active_tree_vertices + 3, active_tree_vertices + 6, normal); + t8_normalize (normal); // Compute sphere's radius over cube root which is the shortest distance to the origin (0,0,0). - const double distance = std::abs (t8_vec_dot (active_tree_vertices, normal)); + const double distance = std::abs (t8_dot (active_tree_vertices, normal)); // Compute actual radius of the sphere. const double SQRT3 = std::sqrt (3.0); const double inner_radius = distance * SQRT3; const double shell_thickness - = std::abs (t8_vec_dot (active_tree_vertices + t8_eclass_num_vertices[active_tree_class] * 3 / 2, normal)) * SQRT3 + = std::abs (t8_dot (active_tree_vertices + t8_eclass_num_vertices[active_tree_class] * 3 / 2, normal)) * SQRT3 - inner_radius; // Compute orthogonal coordinate system anchored on the cmesh element. - t8_vec_orthogonal_tripod (normal, tangent1, tangent2); + t8_orthogonal_tripod (normal, tangent1, tangent2); // Compute anchor of the tripod on the cmesh element's plane. double anchor[3]; - t8_vec_axy (normal, anchor, distance); + t8_axy (normal, anchor, distance); t8_eclass_t interpolation_eclass; switch (active_tree_class) { @@ -246,19 +246,19 @@ t8_geometry_cubed_spherical_shell::t8_geom_evaluate (t8_cmesh_t cmesh, t8_gloidx // Compute difference vector between position and tripod's anchor. double diff_vec[3]; - t8_vec_diff (position, anchor, diff_vec); + t8_diff (position, anchor, diff_vec); // Compute the coefficients of the difference vector in the local // coordinate system of the tripod and apply equi-angular correction. - const double alpha1 = distance * tan (0.25 * M_PI * t8_vec_dot (tangent1, diff_vec) / distance); - const double alpha2 = distance * tan (0.25 * M_PI * t8_vec_dot (tangent2, diff_vec) / distance); + const double alpha1 = distance * tan (0.25 * M_PI * t8_dot (tangent1, diff_vec) / distance); + const double alpha2 = distance * tan (0.25 * M_PI * t8_dot (tangent2, diff_vec) / distance); // Compute the final transformed coordinates. double *out_vec = out_coords + offset_3d; - t8_vec_copy (anchor, out_vec); - t8_vec_axpy (tangent1, out_vec, alpha1); - t8_vec_axpy (tangent2, out_vec, alpha2); - t8_vec_rescale (out_vec, inner_radius + ref_coords[offset_3d + 2] * shell_thickness); + t8_copy (anchor, out_vec); + t8_axpy (tangent1, out_vec, alpha1); + t8_axpy (tangent2, out_vec, alpha2); + t8_rescale (out_vec, inner_radius + ref_coords[offset_3d + 2] * shell_thickness); } } @@ -280,13 +280,13 @@ t8_geometry_cubed_sphere::t8_geom_evaluate (t8_cmesh_t cmesh, t8_gloidx_t gtreei return; } - t8_vec_copy (active_tree_vertices, n); - t8_vec_normalize (n); + t8_copy (active_tree_vertices, n); + t8_normalize (n); - t8_vec_copy (active_tree_vertices + 7 * 3, r); - t8_vec_normalize (r); + t8_copy (active_tree_vertices + 7 * 3, r); + t8_normalize (r); - const double inv_denominator = 1.0 / t8_vec_dot (r, n); + const double inv_denominator = 1.0 / t8_dot (r, n); /* Radial reference coordinate index. */ const int r_coord_lookup[4] = { -1, 1, 0, 2 }; @@ -317,17 +317,17 @@ t8_geometry_cubed_sphere::t8_geom_evaluate (t8_cmesh_t cmesh, t8_gloidx_t gtreei /* Compute and normalize vector `s`. */ t8_geom_linear_interpolation (corr_ref_coords, active_tree_vertices, 3, 3, s); - t8_vec_normalize (s); + t8_normalize (s); } t8_geom_linear_interpolation (ref_coords + offset, active_tree_vertices, 3, 3, p); /* Compute intersection of line with a plane. */ - const double out_radius = t8_vec_dot (p, n) * inv_denominator; + const double out_radius = t8_dot (p, n) * inv_denominator; /* Linear blend from flat to curved: `out_coords = (1.0 - r_ref)*p + r_ref_ * out_radius * s`. */ - t8_vec_axy (p, out_coords + offset, 1.0 - r_ref); - t8_vec_axpy (s, out_coords + offset, r_ref * out_radius); + t8_axy (p, out_coords + offset, 1.0 - r_ref); + t8_axpy (s, out_coords + offset, r_ref * out_radius); } } diff --git a/src/t8_geometry/t8_geometry_implementations/t8_geometry_linear.cxx b/src/t8_geometry/t8_geometry_implementations/t8_geometry_linear.cxx index 0f20da6443..3bc69c1ae3 100644 --- a/src/t8_geometry/t8_geometry_implementations/t8_geometry_linear.cxx +++ b/src/t8_geometry/t8_geometry_implementations/t8_geometry_linear.cxx @@ -24,7 +24,7 @@ #include #include #include -#include +#include t8_geometry_linear::t8_geometry_linear (): t8_geometry_with_vertices ("t8_geom_linear") { @@ -52,7 +52,7 @@ t8_geometry_linear::t8_geom_evaluate_jacobian (t8_cmesh_t cmesh, t8_gloidx_t gtr /* Test whether four given points in 3D are coplanar up to a given tolerance. */ static int -t8_four_points_coplanar (const double p_0[3], const double p_1[3], const double p_2[3], const double p_3[3], +t8_four_points_coplanar (const t8_3D_vec p_0, const t8_3D_vec p_1, const t8_3D_vec p_2, const t8_3D_vec p_3, const double tolerance) { /* Let p0, p1, p2, p3 be the four points. @@ -65,31 +65,31 @@ t8_four_points_coplanar (const double p_0[3], const double p_1[3], const double * Hence we check if || n1 x n2 || < tolerance. */ /* A = p1 - p0 */ - double A[3]; - t8_vec_axpyz (p_0, p_1, A, -1); + t8_3D_vec A; + t8_axpyz (p_0, p_1, A, -1); /* B = p2 - p0 */ - double B[3]; - t8_vec_axpyz (p_0, p_2, B, -1); + t8_3D_vec B; + t8_axpyz (p_0, p_2, B, -1); /* C = p3 - p0 */ - double C[3]; - t8_vec_axpyz (p_0, p_3, C, -1); + t8_3D_vec C; + t8_axpyz (p_0, p_3, C, -1); /* n1 = A x B */ - double A_cross_B[3]; - t8_vec_cross (A, B, A_cross_B); + t8_3D_vec A_cross_B; + t8_cross_3D (A, B, A_cross_B); /* n2 = A x C */ - double A_cross_C[3]; - t8_vec_cross (A, C, A_cross_C); + t8_3D_vec A_cross_C; + t8_cross_3D (A, C, A_cross_C); /* n1 x n2 */ - double n1_cross_n2[3]; - t8_vec_cross (A_cross_B, A_cross_C, n1_cross_n2); + t8_3D_vec n1_cross_n2; + t8_cross_3D (A_cross_B, A_cross_C, n1_cross_n2); /* || n1 x n2 || */ - const double norm = t8_vec_norm (n1_cross_n2); + const double norm = t8_norm (n1_cross_n2); return norm < tolerance; } #endif @@ -123,27 +123,27 @@ t8_geometry_linear::t8_geom_point_batch_inside_element (t8_forest_t forest, t8_l * (p_1 - p_0)x = p - p_0 * has a solution x with 0 <= x <= 1 */ - double p_0[3], v[3]; + t8_3D_vec p_0, v; /* Compute the vertex coordinates of the line */ - t8_forest_element_coordinate (forest, ltreeid, element, 0, p_0); + t8_forest_element_coordinate (forest, ltreeid, element, 0, p_0.data ()); /* v = p_1 */ - t8_forest_element_coordinate (forest, ltreeid, element, 1, v); + t8_forest_element_coordinate (forest, ltreeid, element, 1, v.data ()); /* v = p_1 - p_0 */ - t8_vec_axpy (p_0, v, -1); + t8_axpy (p_0, v, -1); for (int ipoint = 0; ipoint < num_points; ipoint++) { - is_inside[ipoint] = t8_line_point_inside (p_0, v, &points[ipoint * 3], tolerance); + is_inside[ipoint] = t8_line_point_inside (p_0.data (), v.data (), &points[ipoint * 3], tolerance); } return; } case T8_ECLASS_QUAD: { /* We divide the quad in two triangles and use the triangle check. */ - double p_0[3], p_1[3], p_2[3], p_3[3]; + t8_3D_vec p_0, p_1, p_2, p_3; /* Compute the vertex coordinates of the quad */ - t8_forest_element_coordinate (forest, ltreeid, element, 0, p_0); - t8_forest_element_coordinate (forest, ltreeid, element, 1, p_1); - t8_forest_element_coordinate (forest, ltreeid, element, 2, p_2); - t8_forest_element_coordinate (forest, ltreeid, element, 3, p_3); + t8_forest_element_coordinate (forest, ltreeid, element, 0, p_0.data ()); + t8_forest_element_coordinate (forest, ltreeid, element, 1, p_1.data ()); + t8_forest_element_coordinate (forest, ltreeid, element, 2, p_2.data ()); + t8_forest_element_coordinate (forest, ltreeid, element, 3, p_3.data ()); #if T8_ENABLE_DEBUG /* Issue a warning if the points of the quad do not lie in the same plane */ @@ -151,45 +151,46 @@ t8_geometry_linear::t8_geom_point_batch_inside_element (t8_forest_t forest, t8_l t8_debugf ("WARNING: Testing if point is inside a quad that is not coplanar. This test will be inaccurate.\n"); } #endif - double v[3]; - double w[3]; + t8_3D_vec v; + t8_3D_vec w; /* v = v - p_0 = p_1 - p_0 */ - t8_vec_axpyz (p_0, p_1, v, -1); + t8_axpyz (p_0, p_1, v, -1); /* w = w - p_0 = p_2 - p_0 */ - t8_vec_axpyz (p_0, p_2, w, -1); + t8_axpyz (p_0, p_2, w, -1); /* Check whether the point is inside the first triangle. */ for (int ipoint = 0; ipoint < num_points; ipoint++) { - is_inside[ipoint] = t8_triangle_point_inside (p_0, v, w, &points[ipoint * 3], tolerance); + is_inside[ipoint] = t8_triangle_point_inside (p_0.data (), v.data (), w.data (), &points[ipoint * 3], tolerance); } /* If not, check whether the point is inside the second triangle. */ /* v = v - p_0 = p_1 - p_0 */ - t8_vec_axpyz (p_1, p_2, v, -1); + t8_axpyz (p_1, p_2, v, -1); /* w = w - p_0 = p_2 - p_0 */ - t8_vec_axpyz (p_1, p_3, w, -1); + t8_axpyz (p_1, p_3, w, -1); for (int ipoint = 0; ipoint < num_points; ipoint++) { if (!is_inside[ipoint]) { /* point_inside is true if the point was inside the first or second triangle. Otherwise it is false. */ - is_inside[ipoint] = t8_triangle_point_inside (p_1, v, w, &points[ipoint * 3], tolerance); + is_inside[ipoint] + = t8_triangle_point_inside (p_1.data (), v.data (), w.data (), &points[ipoint * 3], tolerance); } } return; } case T8_ECLASS_TRIANGLE: { - double p_0[3], p_1[3], p_2[3]; + t8_3D_vec p_0, p_1, p_2; /* Compute the vertex coordinates of the triangle */ - t8_forest_element_coordinate (forest, ltreeid, element, 0, p_0); - t8_forest_element_coordinate (forest, ltreeid, element, 1, p_1); - t8_forest_element_coordinate (forest, ltreeid, element, 2, p_2); - double v[3]; - double w[3]; + t8_forest_element_coordinate (forest, ltreeid, element, 0, p_0.data ()); + t8_forest_element_coordinate (forest, ltreeid, element, 1, p_1.data ()); + t8_forest_element_coordinate (forest, ltreeid, element, 2, p_2.data ()); + t8_3D_vec v; + t8_3D_vec w; /* v = v - p_0 = p_1 - p_0 */ - t8_vec_axpyz (p_0, p_1, v, -1); + t8_axpyz (p_0, p_1, v, -1); /* w = w - p_0 = p_2 - p_0 */ - t8_vec_axpyz (p_0, p_2, w, -1); + t8_axpyz (p_0, p_2, w, -1); for (int ipoint = 0; ipoint < num_points; ipoint++) { - is_inside[ipoint] = t8_triangle_point_inside (p_0, v, w, &points[ipoint * 3], tolerance); + is_inside[ipoint] = t8_triangle_point_inside (p_0.data (), v.data (), w.data (), &points[ipoint * 3], tolerance); } return; } diff --git a/src/t8_geometry/t8_geometry_with_vertices.cxx b/src/t8_geometry/t8_geometry_with_vertices.cxx index b2f4590e01..894fae0c0c 100644 --- a/src/t8_geometry/t8_geometry_with_vertices.cxx +++ b/src/t8_geometry/t8_geometry_with_vertices.cxx @@ -26,7 +26,7 @@ #include #include -#include +#include /* Load the coordinates of the newly active tree to the active_tree_vertices variable. */ void @@ -74,25 +74,25 @@ t8_geometry_with_vertices::t8_geom_tree_negative_volume () const */ /* build the vectors v_i as vertices_i - vertices_0 */ - double v_1[3], v_2[3], v_j[3], cross[3], sc_prod; - int i, j; + t8_3D_vec v_1, v_2, v_j, cross; + int jvector_source; if (active_tree_class == T8_ECLASS_TET || active_tree_class == T8_ECLASS_PRISM) { /* In the tet/prism case, the third vector is v_3 */ - j = 3; + jvector_source = 3; } else { /* For pyramids and Hexes, the third vector is v_4 */ - j = 4; + jvector_source = 4; } - for (i = 0; i < 3; i++) { - v_1[i] = active_tree_vertices[3 + i] - active_tree_vertices[i]; - v_2[i] = active_tree_vertices[6 + i] - active_tree_vertices[i]; - v_j[i] = active_tree_vertices[3 * j + i] - active_tree_vertices[i]; + for (int dim = 0; dim < T8_ECLASS_MAX_DIM; dim++) { + v_1[dim] = active_tree_vertices[3 + dim] - active_tree_vertices[dim]; + v_2[dim] = active_tree_vertices[6 + dim] - active_tree_vertices[dim]; + v_j[dim] = active_tree_vertices[3 * jvector_source + dim] - active_tree_vertices[dim]; } /* compute cross = v_1 x v_2 */ - t8_vec_cross (v_1, v_2, cross); + t8_cross_3D (v_1, v_2, cross); /* Compute sc_prod = */ - sc_prod = t8_vec_dot (v_j, cross); + double sc_prod = t8_dot (v_j, cross); T8_ASSERT (sc_prod != 0); return active_tree_class == T8_ECLASS_TET ? sc_prod > 0 : sc_prod < 0; diff --git a/src/t8_schemes/t8_default/Makefile.am b/src/t8_schemes/t8_default/Makefile.am index 20ab4f0505..d7a2f2f682 100644 --- a/src/t8_schemes/t8_default/Makefile.am +++ b/src/t8_schemes/t8_default/Makefile.am @@ -10,8 +10,7 @@ libt8_installed_headers_default_common += \ src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx libt8_installed_headers_default_vertex += \ src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.hxx \ - src/t8_schemes/t8_default/t8_default_vertex/t8_dvertex.h \ - src/t8_schemes/t8_default/t8_default_vertex/t8_dvertex_bits.h + src/t8_schemes/t8_default/t8_default_vertex/t8_dvertex.h libt8_installed_headers_default_line += \ src/t8_schemes/t8_default/t8_default_line/t8_default_line.hxx \ src/t8_schemes/t8_default/t8_default_line/t8_dline.h \ @@ -62,7 +61,6 @@ libt8_compiled_sources += \ src/t8_schemes/t8_default/t8_default_tri/t8_dtri_bits.c \ src/t8_schemes/t8_default/t8_default_tri/t8_dtri_connectivity.c \ src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.cxx \ - src/t8_schemes/t8_default/t8_default_vertex/t8_dvertex_bits.c \ src/t8_schemes/t8_default/t8_default_pyramid/t8_default_pyramid.cxx \ src/t8_schemes/t8_default/t8_default_pyramid/t8_dpyramid_bits.c \ src/t8_schemes/t8_default/t8_default_pyramid/t8_dpyramid_connectivity.c diff --git a/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx b/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx index 9d9bdb7d74..0f402a79be 100644 --- a/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx +++ b/src/t8_schemes/t8_default/t8_default_common/t8_default_common.hxx @@ -28,7 +28,7 @@ #define T8_DEFAULT_COMMON_HXX #include -#include +#include #include #include @@ -84,7 +84,7 @@ count_leaves_from_level (const int element_level, const int refinement_level, co } template -class t8_default_scheme_common: public t8_crtp { +class t8_default_scheme_common: public t8_crtp_operator { private: friend TUnderlyingEclassScheme; /** Private constructor which can only be used by derived schemes. diff --git a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx index 222675e2f9..7932ffff70 100644 --- a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx +++ b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.cxx @@ -131,6 +131,16 @@ t8_default_scheme_hex::element_get_face_corner (const t8_element_t *element, con return p8est_face_corners[face][corner]; } +int +t8_default_scheme_hex::element_get_corner_face (const t8_element_t *element, const int corner, const int face) const +{ + T8_ASSERT (element_is_valid (element)); + T8_ASSERT (0 <= corner && corner < P8EST_CHILDREN); + T8_ASSERT (0 <= face && face < T8_DHEX_FACES); + + return p8est_corner_faces[corner][face]; +} + void t8_default_scheme_hex::element_get_child (const t8_element_t *elem, const int childid, t8_element_t *child) const { diff --git a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx index 3e62b8a1f7..5041e99314 100644 --- a/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx +++ b/src/t8_schemes/t8_default/t8_default_hex/t8_default_hex.hxx @@ -212,11 +212,7 @@ class t8_default_scheme_hex: public t8_default_scheme_common #include -#include #include #include #include @@ -193,7 +192,7 @@ t8_default_scheme_line::element_get_boundary_face (const t8_element_t *elem, int /* Since each vertex is the same, we just construct a vertex of the same level * as elem. */ - t8_dvertex_init_linear_id ((t8_dvertex_t *) boundary, element_get_level (elem), 0); + t8_default_scheme_vertex::element_set_linear_id (boundary, element_get_level (elem), 0); } /** Construct the first descendant of an element that touches a given face. */ diff --git a/src/t8_schemes/t8_default/t8_default_line/t8_default_line.hxx b/src/t8_schemes/t8_default/t8_default_line/t8_default_line.hxx index 2bd7eb16d5..c437559983 100644 --- a/src/t8_schemes/t8_default/t8_default_line/t8_default_line.hxx +++ b/src/t8_schemes/t8_default/t8_default_line/t8_default_line.hxx @@ -204,11 +204,11 @@ class t8_default_scheme_line: public t8_default_scheme_common= 0; ichild--) { t8_dprism_child (p, children_at_face[p->tri.type][face * 4 + ichild], children[ichild]); } } else { - for (int ichild = 0; ichild < 4; ichild++) { + for (int ichild = 3; ichild >= 0; ichild--) { t8_dprism_child (p, (face % 3) * 4 + ichild, children[ichild]); } } /* Fill child-indices array */ if (child_indices != NULL) { if (face < 3) { - for (int ichild = 0; ichild < 4; ichild++) { + for (int ichild = 3; ichild >= 0; ichild--) { child_indices[ichild] = children_at_face[p->tri.type][face * 4 + ichild]; } } else { - for (int ichild = 0; ichild < 4; ichild++) { + for (int ichild = 3; ichild >= 0; ichild--) { child_indices[ichild] = (face % 3) * 4 + ichild; } } diff --git a/src/t8_schemes/t8_default/t8_default_quad/t8_default_quad.cxx b/src/t8_schemes/t8_default/t8_default_quad/t8_default_quad.cxx index 99937636cd..92a5a032f9 100644 --- a/src/t8_schemes/t8_default/t8_default_quad/t8_default_quad.cxx +++ b/src/t8_schemes/t8_default/t8_default_quad/t8_default_quad.cxx @@ -490,7 +490,7 @@ t8_default_scheme_quad::element_extrude_face (const t8_element_t *face, t8_eleme p4est_quadrant_t *q = (p4est_quadrant_t *) elem; T8_ASSERT (element_is_valid (elem)); - T8_ASSERT (scheme->element_is_valid (T8_ECLASS_LINE, elem)); + T8_ASSERT (scheme->element_is_valid (T8_ECLASS_LINE, face)); T8_ASSERT (0 <= root_face && root_face < P4EST_FACES); /* * The faces of the root quadrant are enumerated like this: diff --git a/src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.cxx b/src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.cxx index 5c7bea179f..a73c01fe78 100644 --- a/src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.cxx +++ b/src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.cxx @@ -22,10 +22,15 @@ #include #include -#include -/* We want to export the whole implementation to be callable from "C" */ -T8_EXTERN_C_BEGIN (); +/** Print a vertex (unused static helper function for debugging) + * \param [in] v vertex to be considered. + */ +inline static void +t8_dvertex_debug_print (const t8_dvertex_t *v) +{ + t8_debugf ("level: %i\n", v->level); +} size_t t8_default_scheme_vertex::get_element_size (void) const @@ -43,7 +48,7 @@ int t8_default_scheme_vertex::element_get_level (const t8_element_t *elem) const { T8_ASSERT (element_is_valid (elem)); - return t8_dvertex_get_level ((const t8_dvertex_t *) elem); + return ((const t8_dvertex_t *) elem)->level; } void @@ -51,19 +56,19 @@ t8_default_scheme_vertex::element_copy (const t8_element_t *source, t8_element_t { T8_ASSERT (element_is_valid (source)); T8_ASSERT (element_is_valid (dest)); - t8_dvertex_copy ((const t8_dvertex_t *) source, (t8_dvertex_t *) dest); + memcpy ((t8_dvertex_t *) dest, (const t8_dvertex_t *) source, sizeof (t8_dvertex_t)); } int t8_default_scheme_vertex::element_compare (const t8_element_t *elem1, const t8_element_t *elem2) const { - return t8_dvertex_compare ((const t8_dvertex_t *) elem1, (const t8_dvertex_t *) elem2); + return ((const t8_dvertex_t *) elem1)->level - ((const t8_dvertex_t *) elem2)->level; } int t8_default_scheme_vertex::element_is_equal (const t8_element_t *elem1, const t8_element_t *elem2) const { - return t8_dvertex_equal ((const t8_dvertex_t *) elem1, (const t8_dvertex_t *) elem2); + return ((const t8_dvertex_t *) elem1)->level == ((const t8_dvertex_t *) elem2)->level; } void @@ -74,18 +79,20 @@ t8_default_scheme_vertex::element_get_parent (const t8_element_t *elem, t8_eleme T8_ASSERT (element_is_valid (elem)); T8_ASSERT (element_is_valid (parent)); - t8_dvertex_parent (v, p); + T8_ASSERT (v->level > 0); + + /* Set the parent's level */ + p->level = v->level - 1; } void -t8_default_scheme_vertex::element_get_sibling (const t8_element_t *elem, int sibid, t8_element_t *sibling) const +t8_default_scheme_vertex::element_get_sibling (const t8_element_t *elem, const int sibid, t8_element_t *sibling) const { - const t8_dvertex_t *v = (const t8_dvertex_t *) elem; - t8_dvertex_t *s = (t8_dvertex_t *) sibling; - T8_ASSERT (element_is_valid (elem)); T8_ASSERT (element_is_valid (sibling)); - t8_dvertex_sibling (v, sibid, s); + T8_ASSERT (sibid == 0); + + this->element_copy (elem, sibling); } int @@ -124,7 +131,10 @@ t8_default_scheme_vertex::element_get_child (const t8_element_t *elem, int child T8_ASSERT (element_is_valid (child)); T8_ASSERT (childid == 0); - t8_dvertex_child (v, c); + T8_ASSERT (v->level < T8_DVERTEX_MAXLEVEL); + + /* The children level */ + c->level = v->level + 1; } void @@ -137,20 +147,24 @@ t8_default_scheme_vertex::element_get_children (const t8_element_t *elem, int le T8_ASSERT (element_is_valid (c[i])); } #endif - t8_dvertex_childrenpv ((const t8_dvertex_t *) elem, (t8_dvertex_t **) c); + const t8_dvertex_t *v = (const t8_dvertex_t *) elem; + T8_ASSERT (v->level < T8_DVERTEX_MAXLEVEL); + + /* Set the Level, Level increases */ + ((t8_dvertex_t **) c)[0]->level = v->level + 1; } int t8_default_scheme_vertex::element_get_child_id (const t8_element_t *elem) const { T8_ASSERT (element_is_valid (elem)); - return t8_dvertex_child_id ((const t8_dvertex_t *) elem); + return 0; } int t8_default_scheme_vertex::element_get_ancestor_id (const t8_element_t *elem, int level) const { - return t8_dvertex_ancestor_id ((const t8_dvertex_t *) elem, level); + return 0; } int @@ -161,7 +175,7 @@ t8_default_scheme_vertex::elements_are_family (t8_element_t *const *fam) const T8_ASSERT (element_is_valid (fam[i])); } #endif - return t8_dvertex_is_familypv ((const t8_dvertex_t **) fam); + return ((const t8_dvertex_t **) fam)[0]->level > 0; } void @@ -174,7 +188,9 @@ t8_default_scheme_vertex::element_get_nca (const t8_element_t *elem1, const t8_e T8_ASSERT (element_is_valid (elem1)); T8_ASSERT (element_is_valid (elem2)); - t8_dvertex_nearest_common_ancestor (v1, v2, c); + + /* The nca is the one of the two vertices with smaller level */ + c->level = SC_MIN (v1->level, v2->level); } /** Transform the coordinates of a vertex considered as boundary element @@ -186,26 +202,25 @@ t8_default_scheme_vertex::element_transform_face (const t8_element_t *elem1, t8_ T8_ASSERT (element_is_valid (elem1)); T8_ASSERT (element_is_valid (elem2)); - t8_dvertex_transform_face ((const t8_dvertex_t *) elem1, (t8_dvertex_t *) elem2); + ((t8_dvertex_t *) elem2)->level = ((const t8_dvertex_t *) elem1)->level; } int t8_default_scheme_vertex::element_is_root_boundary (const t8_element_t *elem, int face) const { - const t8_dvertex_t *v = (const t8_dvertex_t *) elem; - T8_ASSERT (element_is_valid (elem)); - return t8_dvertex_is_root_boundary (v, face); + return 1; } void -t8_default_scheme_vertex::element_set_linear_id (t8_element_t *elem, int level, t8_linearidx_t id) const +t8_default_scheme_vertex::element_set_linear_id (t8_element_t *elem, const int level, const t8_linearidx_t id) { T8_ASSERT (0 <= level && level <= T8_DVERTEX_MAXLEVEL); - T8_ASSERT (0 <= id && id < ((t8_linearidx_t) 1) << 3 * level); + T8_ASSERT (0 == id); T8_ASSERT (element_is_valid (elem)); - t8_dvertex_init_linear_id ((t8_dvertex_t *) elem, level, id); + /* Set the level */ + ((t8_dvertex_t *) elem)->level = level; } t8_linearidx_t @@ -214,7 +229,7 @@ t8_default_scheme_vertex::element_get_linear_id (const t8_element_t *elem, int l T8_ASSERT (element_is_valid (elem)); T8_ASSERT (0 <= level && level <= T8_DVERTEX_MAXLEVEL); - return t8_dvertex_linear_id ((const t8_dvertex_t *) elem, level); + return 0; } void @@ -223,7 +238,9 @@ t8_default_scheme_vertex::element_get_first_descendant (const t8_element_t *elem T8_ASSERT (element_is_valid (elem)); T8_ASSERT (element_is_valid (desc)); T8_ASSERT (0 <= level && level <= T8_DVERTEX_MAXLEVEL); - t8_dvertex_first_descendant ((const t8_dvertex_t *) elem, (t8_dvertex_t *) desc, level); + T8_ASSERT (level >= ((const t8_dvertex_t *) elem)->level); + + ((t8_dvertex_t *) desc)->level = level; } void @@ -232,7 +249,9 @@ t8_default_scheme_vertex::element_get_last_descendant (const t8_element_t *elem, T8_ASSERT (element_is_valid (elem)); T8_ASSERT (element_is_valid (desc)); T8_ASSERT (0 <= level && level <= T8_DVERTEX_MAXLEVEL); - t8_dvertex_last_descendant ((const t8_dvertex_t *) elem, (t8_dvertex_t *) desc, level); + T8_ASSERT (level >= ((const t8_dvertex_t *) elem)->level); + + ((t8_dvertex_t *) desc)->level = level; } void @@ -249,7 +268,9 @@ void t8_default_scheme_vertex::element_get_vertex_integer_coords (const t8_element_t *elem, int vertex, int coords[]) const { T8_ASSERT (element_is_valid (elem)); - t8_dvertex_vertex_integer_coords ((const t8_dvertex_t *) elem, vertex, coords); + T8_ASSERT (vertex == 0); + + coords[0] = 0; } void @@ -259,7 +280,7 @@ t8_default_scheme_vertex::element_get_vertex_reference_coords (const t8_element_ T8_ASSERT (element_is_valid (elem)); T8_ASSERT (vertex == 0); - t8_dvertex_vertex_ref_coords ((const t8_dvertex_t *) elem, vertex, coords); + coords[0] = 0; } void @@ -267,15 +288,20 @@ t8_default_scheme_vertex::element_get_reference_coords (const t8_element_t *elem const size_t num_coords, double *out_coords) const { T8_ASSERT (element_is_valid (elem)); - t8_dvertex_compute_reference_coords ((const t8_dvertex_t *) elem, ref_coords, num_coords, out_coords); + T8_ASSERT (fabs (ref_coords[0]) <= T8_PRECISION_EPS); + + for (size_t coord = 0; coord < num_coords; ++coord) { + out_coords[coord] = 0; + } } #ifdef T8_ENABLE_DEBUG int -t8_default_scheme_vertex::element_is_valid (const t8_element_t *elem) const +t8_default_scheme_vertex::element_is_valid (const t8_element_t *elem) { - return t8_dvertex_is_valid ((const t8_dvertex_t *) elem); + const t8_dvertex *v = (const t8_dvertex_t *) elem; + return 0 <= v->level && v->level <= T8_DVERTEX_MAXLEVEL; } void @@ -317,7 +343,7 @@ t8_default_scheme_vertex::element_init (int length, t8_element_t *elem) const #ifdef T8_ENABLE_DEBUG t8_dvertex_t *vertexs = (t8_dvertex_t *) elem; for (int i = 0; i < length; i++) { - t8_dvertex_init (vertexs + i); + vertexs[i].level = 0; } #endif } @@ -368,5 +394,3 @@ t8_default_scheme_vertex::element_MPI_Unpack (void *recvbuf, const int buffer_si SC_CHECK_MPI (mpiret); } } - -T8_EXTERN_C_END (); diff --git a/src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.hxx b/src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.hxx index 291a564a34..9495d5cd98 100644 --- a/src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.hxx +++ b/src/t8_schemes/t8_default/t8_default_vertex/t8_default_vertex.hxx @@ -20,19 +20,12 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** \file t8_default_vertex.h - * The default implementation for vertices. Interface between the - * \file t8_default_common.hxx definitions and the element type specific - * implementations in \file t8_dvertex_bits.h - */ - #ifndef T8_DEFAULT_VERTEX_HXX #define T8_DEFAULT_VERTEX_HXX #include #include #include -#include /* Forward declaration of the scheme so we can use it as an argument in the eclass schemes function. */ class t8_scheme; @@ -197,11 +190,12 @@ class t8_default_scheme_vertex: public t8_default_scheme_common - -int -t8_dvertex_get_level (const t8_dvertex_t *v) -{ - return v->level; -} - -void -t8_dvertex_copy (const t8_dvertex_t *v, t8_dvertex_t *dest) -{ - memcpy (dest, v, sizeof (t8_dvertex_t)); -} - -int -t8_dvertex_equal (const t8_dvertex_t *elem1, const t8_dvertex_t *elem2) -{ - return elem1->level == elem2->level; -} - -int -t8_dvertex_compare (const t8_dvertex_t *v1, const t8_dvertex_t *v2) -{ - /* The vertex with the smaller level - * is considered smaller */ - return v1->level - v2->level; -} - -void -t8_dvertex_parent (const t8_dvertex_t *v, t8_dvertex_t *parent) -{ - T8_ASSERT (v->level > 0); - - /* Set the parent's level */ - parent->level = v->level - 1; -} - -void -t8_dvertex_child (const t8_dvertex_t *v, t8_dvertex_t *child) -{ - T8_ASSERT (v->level < T8_DVERTEX_MAXLEVEL); - - /* The children level */ - child->level = v->level + 1; -} - -void -t8_dvertex_nearest_common_ancestor (const t8_dvertex_t *v1, const t8_dvertex_t *v2, t8_dvertex_t *r) -{ - /* The nca is the one of the two vertices with smaller level */ - r->level = SC_MIN (v1->level, v2->level); -} - -int -t8_dvertex_ancestor_id (const t8_dvertex_t *v, int level) -{ - /* There is only one possible child id, 0 */ - return 0; -} - -int -t8_dvertex_child_id (const t8_dvertex_t *v) -{ - /* There is only one possible child id, 0 */ - return 0; -} - -void -t8_dvertex_sibling (const t8_dvertex_t *v, int sibid, t8_dvertex_t *s) -{ - T8_ASSERT (sibid == 0); - - t8_dvertex_copy (v, s); -} - -void -t8_dvertex_childrenpv (const t8_dvertex_t *v, t8_dvertex_t *c[T8_DVERTEX_CHILDREN]) -{ - T8_ASSERT (v->level < T8_DVERTEX_MAXLEVEL); - - /* Set the Level, Level increases */ - c[0]->level = v->level + 1; -} - -int -t8_dvertex_is_familypv (const t8_dvertex_t *f[]) -{ - /* A vertex with level greater 0 is always a family */ - return f[0]->level > 0; -} - -int -t8_dvertex_is_root_boundary (const t8_dvertex_t *v, int face) -{ - /* A vertex is always at the root boundary */ - return 1; -} - -int -t8_dvertex_is_inside_root (const t8_dvertex_t *v) -{ - /* A vertex is always inside root */ - return 1; -} - -void -t8_dvertex_init_linear_id (t8_dvertex_t *v, int level, t8_linearidx_t id) -{ - T8_ASSERT (0 <= level && level <= T8_DVERTEX_MAXLEVEL); - T8_ASSERT (0 == id); - - /* Set the level */ - v->level = level; -} - -void -t8_dvertex_transform_face (const t8_dvertex_t *vertex1, t8_dvertex_t *vertex2) -{ - /* The transformed vertex is the same vertex */ - vertex2->level = vertex1->level; -} - -void -t8_dvertex_first_descendant (const t8_dvertex_t *v, t8_dvertex_t *s, int level) -{ - T8_ASSERT (level >= v->level && level <= T8_DVERTEX_MAXLEVEL); - - s->level = level; -} - -void -t8_dvertex_last_descendant (const t8_dvertex_t *v, t8_dvertex_t *s, int level) -{ - T8_ASSERT (level >= v->level && level <= T8_DVERTEX_MAXLEVEL); - - s->level = level; -} - -void -t8_dvertex_vertex_integer_coords (const t8_dvertex_t *elem, const int vertex, int coords[]) -{ - T8_ASSERT (vertex == 0); - - coords[0] = 0; -} - -void -t8_dvertex_vertex_ref_coords (const t8_dvertex_t *elem, const int vertex, double coords[]) -{ - T8_ASSERT (vertex == 0); - - coords[0] = 0; -} - -void -t8_dvertex_compute_reference_coords (const t8_dvertex_t *elem, const double *ref_coords, const size_t num_coords, - double *out_coords) -{ - T8_ASSERT (fabs (ref_coords[0]) <= T8_PRECISION_EPS); - T8_ASSERT (t8_dvertex_is_valid (elem)); - for (size_t coord = 0; coord < num_coords; ++coord) { - out_coords[coord] = 0; - } -} - -t8_linearidx_t -t8_dvertex_linear_id (const t8_dvertex_t *elem, int level) -{ - T8_ASSERT (level <= T8_DVERTEX_MAXLEVEL && level >= 0); - - return 0; -} - -int -t8_dvertex_is_valid (const t8_dvertex_t *v) -{ - /* A vertex is valid if its level is in the valid range */ - return 0 <= v->level && v->level <= T8_DVERTEX_MAXLEVEL; -} - -void -t8_dvertex_debug_print (const t8_dvertex_t *v) -{ - t8_debugf ("level: %i\n", v->level); -} - -void -t8_dvertex_init (t8_dvertex_t *v) -{ - v->level = 0; -} diff --git a/src/t8_schemes/t8_default/t8_default_vertex/t8_dvertex_bits.h b/src/t8_schemes/t8_default/t8_default_vertex/t8_dvertex_bits.h deleted file mode 100644 index f205adecbe..0000000000 --- a/src/t8_schemes/t8_default/t8_default_vertex/t8_dvertex_bits.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - This file is part of t8code. - t8code is a C library to manage a collection (a forest) of multiple - connected adaptive space-trees of general element classes in parallel. - - Copyright (C) 2015 the developers - - t8code 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. - - t8code 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 t8code; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/** \file t8_dvertex_bits.h - * Definitions of vertex-specific functions. - */ - -#ifndef T8_DVERTEX_BITS_H -#define T8_DVERTEX_BITS_H - -#include -#include - -T8_EXTERN_C_BEGIN (); - -/** Compute the level of a vertex. - * \param [in] l vertex whose level is computed. - * \return The level of \a l. - */ -int -t8_dvertex_get_level (const t8_dvertex_t *v); - -/** Copy all values from one vertex to another. - * \param [in] l The vertex to be copied. - * \param [in,out] dest Existing vertex whose data will be filled with the data - * of \a l. - */ -void -t8_dvertex_copy (const t8_dvertex_t *v, t8_dvertex_t *dest); - -/** Compare two elements. returns negative if l1 < l2, zero if l1 equals l2 - * and positive if l1 > l2. - * If l2 is a copy of l1 then the elements are equal. - */ -int -t8_dvertex_compare (const t8_dvertex_t *l1, const t8_dvertex_t *l2); - -/** Check if two elements are equal. -* \param [in] elem1 The first element. -* \param [in] elem2 The second element. -* \return 1 if the elements are equal, 0 if they are not equal -*/ -int -t8_dvertex_equal (const t8_dvertex_t *elem1, const t8_dvertex_t *elem2); - -/** Compute the parent of a vertex. - * \param [in] l The input vertex. - * \param [in,out] parent Existing vertex whose data will be filled with the parent - * data of \a l. - */ -void -t8_dvertex_parent (const t8_dvertex_t *v, t8_dvertex_t *parent); - -/** Compute the childid-th child in Morton order of a vertex. - * \param [in] l Input vertex. - * \param [in] childid The id of the child, 0 or 1, in Morton order. - * \param [in,out] child Existing vertex whose data will be filled - * with the date of l's childid-th child. - */ -void -t8_dvertex_child (const t8_dvertex_t *v, t8_dvertex_t *child); - -/** Computes the nearest common ancestor of two vertexs in the same tree. - * \param [in] l1 First input vertex. - * \param [in] l2 Second input vertex. - * \param [in,out] r Existing vertex whose data will be filled. - * \note \a l1, \a l2, \a r may point to the same vertex. - */ -void -t8_dvertex_nearest_common_ancestor (const t8_dvertex_t *t1, const t8_dvertex_t *t2, t8_dvertex_t *r); - -/** Compute the position of the ancestor of this child at level \a level within - * its siblings. - * \param [in] l vertex to be considered. - * \param [in] level level to be considered. - * \return Returns its child id 0 or 1. - */ -int -t8_dvertex_ancestor_id (const t8_dvertex_t *v, int level); - -/** Compute the position of the ancestor of this child at level \a level within - * its siblings. - * \param [in] t vertex to be considered. - * \return Returns its child id in 0,1 - */ -int -t8_dvertex_child_id (const t8_dvertex_t *t); - -/** Compute the sibling of a vertex. - * \param [in] v vertex to be considered. - * \param [in] sibid The id of the sibling, must be 0. - * \param [out] s The sibling of \a v. For vertices \a s is just a copy of \a v. - */ -void -t8_dvertex_sibling (const t8_dvertex_t *v, int sibid, t8_dvertex_t *s); - -/** Compute the 2 children of a vertex, array version. - * \param [in] t Input vertex. - * \param [in,out] c Pointers to the 2 computed children in Morton order. - * t may point to the same quadrant as c[0]. - */ -void -t8_dvertex_childrenpv (const t8_dvertex_t *t, t8_dvertex_t *c[T8_DVERTEX_CHILDREN]); - -/** Check whether a collection of two vertexs is a family in Morton order. - * \param [in] f An array of two vertexs. - * \return Nonzero if \a f is a family of vertexs. - */ -int -t8_dvertex_is_familypv (const t8_dvertex_t *f[]); - -/** Compute whether a given vertex shares a given face with its root tree. - * \param [in] p The input vertex. - * \param [in] face A face of \a p. - * \return True if \a face is a subface of the vertex's root element. - */ -int -t8_dvertex_is_root_boundary (const t8_dvertex_t *p, int face); - -/** Test if a vertex lies inside of the root vertex, - * that is the vertex of level 0, anchor node (0,0) - * \param [in] l Input vertex. - * \return true If \a l lies inside of the root vertex. - */ -int -t8_dvertex_is_inside_root (const t8_dvertex_t *v); - -/** Initialize a vertex as the vertex with a given global id in a uniform - * refinement of a given level. * - * \param [in,out] l Existing vertex whose data will be filled. - * \param [in] id Index to be considered. - * \param [in] level level of uniform grid to be considered. - */ -void -t8_dvertex_init_linear_id (t8_dvertex_t *v, int level, t8_linearidx_t id); - -/** Suppose we have two trees that share a common vertex face f. - * Given a vertex e that is a subface of f in one of the trees - * and given the orientation of the tree connection, construct the face - * vertex of the respective tree neighbor that logically coincides with e - * but lies in the coordinate system of the neighbor tree. - * \param [in] elem1 The face element. - * \param [in,out] elem2 On return the face element \a elem1 with respect - * to the coordinate system of the other tree. - * \note For vertices this function is equivalent to copy. - */ -void -t8_dvertex_transform_face (const t8_dvertex_t *vertex1, t8_dvertex_t *vertex2); - -/** Compute the first descendant of a vertex at a given level. This is the descendant of - * the vertex in a uniform level refinement that has the smallest id. - * \param [in] l vertex whose descendant is computed. - * \param [out] s Existing vertex whose data will be filled with the data - * of \a l's first descendant on level \a level. - * \param [in] level The refinement level. Must be greater than \a l's refinement - * level. - */ -void -t8_dvertex_first_descendant (const t8_dvertex_t *v, t8_dvertex_t *s, int level); - -/** Compute the last descendant of a vertex at a given level. This is the descendant of - * the vertex in a uniform level refinement that has the largest id. - * \param [in] l vertex whose descendant is computed. - * \param [out] s Existing vertex whose data will be filled with the data - * of \a l's last descendant on level \a level. - * \param [in] level The refinement level. Must be greater than \a l's refinement - * level. - */ -void -t8_dvertex_last_descendant (const t8_dvertex_t *v, t8_dvertex_t *s, int level); - -/** Compute the coordinates of a vertex (always 0). - * \param [in] elem vertex whose vertex is computed. - * \param [in] vertex The number of the vertex of \a elem - * \param [out] coords The coordinates of the computed vertex - */ -void -t8_dvertex_vertex_integer_coords (const t8_dvertex_t *elem, int vertex, int coords[]); - -/** Compute the coordinates of a vertex (always 0) inside the [0,1]^0 reference space. - * \param [in] elem vertex whose vertex is computed. - * \param [in] vertex The number of the vertex of \a elem (must be 0). - * \param [out] coords The coordinates of the computed vertex, must have one entry (will be set to 0). - */ -void -t8_dvertex_vertex_ref_coords (const t8_dvertex_t *elem, int vertex, double coords[]); - -/** Convert points in the reference space of a vertex element to points in the - * reference space of the tree (level 0) embedded in \f$ [0,1]^1 \f$. - * \param [in] elem Input vertex. - * \param [in] ref_coords The reference coordinates in the vertex - * (\a num_coords times \f$ [0,1]^1 \f$) - * \param [in] num_coords Number of coordinates to evaluate - * \param [out] out_coords An array of \a num_coords x 1 x double that - * will be filled with the reference coordinates - * of the points on the vertex (will be set to 0). - */ -void -t8_dvertex_compute_reference_coords (const t8_dvertex_t *elem, const double *ref_coords, const size_t num_coords, - double *out_coords); - -/** Computes the linear position of a vertex in an uniform grid. - * \param [in] vertex vertex whose id will be computed. - * \return Returns the linear position of this vertex on a grid. - */ -t8_linearidx_t -t8_dvertex_linear_id (const t8_dvertex_t *elem, int level); - -/** Query whether all entries of a vertex are in valid ranges. - * \param [in] l vertex to be considered. - * \return True, if \a l is a valid vertex and it is safe to call any - * function in this file on \a l. - * False otherwise. - */ -int -t8_dvertex_is_valid (const t8_dvertex_t *v); - -/** Print a vertex - * \param [in] v vertex to be considered. - */ -void -t8_dvertex_debug_print (const t8_dvertex_t *v); - -/** Set default values for a vertex, such that it passes \ref t8_dvertex_is_valid. - * \param [in] l vertex to be initialized - */ -void -t8_dvertex_init (t8_dvertex_t *v); - -T8_EXTERN_C_END (); - -#endif /* T8_DVERTEX_BITS_H */ diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index dac78e4835..ff0d77f482 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #if T8_ENABLE_DEBUG // Only needed for t8_debug_print_type @@ -92,7 +94,11 @@ class t8_scheme { t8_default_scheme_hex, t8_default_scheme_tet, t8_default_scheme_prism, - t8_default_scheme_pyramid + t8_default_scheme_pyramid, + t8_standalone_scheme, + t8_standalone_scheme, + t8_standalone_scheme, + t8_standalone_scheme >; /* clang-format on */ diff --git a/src/t8_schemes/t8_standalone/t8_standalone.cxx b/src/t8_schemes/t8_standalone/t8_standalone.cxx new file mode 100644 index 0000000000..b66f35a2b3 --- /dev/null +++ b/src/t8_schemes/t8_standalone/t8_standalone.cxx @@ -0,0 +1,72 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include + +/* We want to export the whole implementation to be callable from "C" */ +T8_EXTERN_C_BEGIN (); + +const t8_scheme * +t8_scheme_new_standalone (void) +{ + t8_scheme_builder builder; + + builder.add_eclass_scheme> (); + builder.add_eclass_scheme> (); + builder.add_eclass_scheme> (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme> (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + // builder.add_eclass_scheme> (); //ADD if PYRAMID in "element_get_shape" + return builder.build_scheme (); +} + +int +t8_eclass_scheme_is_standalone (const t8_scheme *scheme, const t8_eclass_t eclass) +{ + switch (eclass) { + case T8_ECLASS_VERTEX: + return scheme->check_eclass_scheme_type> (T8_ECLASS_VERTEX); + case T8_ECLASS_LINE: + return scheme->check_eclass_scheme_type> (T8_ECLASS_LINE); + case T8_ECLASS_QUAD: + return scheme->check_eclass_scheme_type> (T8_ECLASS_QUAD); + // case T8_ECLASS_TRIANGLE: + // return scheme->check_eclass_scheme_type> (T8_ECLASS_TRIANGLE); + case T8_ECLASS_HEX: + return scheme->check_eclass_scheme_type> (T8_ECLASS_HEX); + // case T8_ECLASS_TET: + // return scheme->check_eclass_scheme_type> (T8_ECLASS_TET); + // case T8_ECLASS_PRISM: + // return scheme->check_eclass_scheme_type> (T8_ECLASS_PRISM); + // case T8_ECLASS_PYRAMID: + // return scheme->check_eclass_scheme_type> (T8_ECLASS_PYRAMID); + default: + SC_ABORT_NOT_REACHED (); + } + return 0; /* Default return value false */ +} + +T8_EXTERN_C_END (); diff --git a/src/t8_schemes/t8_crtp.hxx b/src/t8_schemes/t8_standalone/t8_standalone.hxx similarity index 53% rename from src/t8_schemes/t8_crtp.hxx rename to src/t8_schemes/t8_standalone/t8_standalone.hxx index 1c9e1467d6..c59a1099f9 100644 --- a/src/t8_schemes/t8_crtp.hxx +++ b/src/t8_schemes/t8_standalone/t8_standalone.hxx @@ -3,7 +3,7 @@ t8code is a C library to manage a collection (a forest) of multiple connected adaptive space-trees of general element classes in parallel. - Copyright (C) 2024 the developers + Copyright (C) 2025 the developers t8code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,29 +20,27 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** \file t8_crtp.hxx - * This file implements a helper class for CRTP implementations. - */ +#ifndef T8_STANDALONE_HXX +#define T8_STANDALONE_HXX + +#include +#include + +T8_EXTERN_C_BEGIN (); -#ifndef T8_CRTP_HXX -#define T8_CRTP_HXX +/** Return the standalone element implementation of t8code. */ +const t8_scheme * +t8_scheme_new_standalone (void); -/** CRTP helper class, adds static "upcasting" methods for const and non const objects. - * \tparam TUnderlying The CRTP derived class. +/** Check whether a given eclass_scheme is one of the standalone schemes. + * \param [in] scheme A (pointer to a) scheme + * \param [in] eclass The eclass to check + * \return True (non-zero) if \a scheme is one of the default schemes, + * false (zero) otherwise. */ -template -class t8_crtp { - public: - inline TUnderlying& - underlying () - { - return static_cast (*this); - } - inline TUnderlying const& - underlying () const - { - return static_cast (*this); - } -}; - -#endif /* !T8_CRTP_HXX */ +int +t8_eclass_scheme_is_standalone (const t8_scheme *scheme, const t8_eclass_t eclass); + +T8_EXTERN_C_END (); + +#endif /* !T8_STANDALONE_HXX */ diff --git a/src/t8_schemes/t8_standalone/t8_standalone_elements.hxx b/src/t8_schemes/t8_standalone/t8_standalone_elements.hxx new file mode 100644 index 0000000000..79fae8ad7f --- /dev/null +++ b/src/t8_schemes/t8_standalone/t8_standalone_elements.hxx @@ -0,0 +1,63 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef T8_STANDALONE_ELEMENTS_HXX +#define T8_STANDALONE_ELEMENTS_HXX + +#include +#include +#include + +#define t8_standalone_element t8_standalone + +constexpr uint8_t T8_ELEMENT_DIM[T8_ECLASS_COUNT] = { 0, 1, 2, 2, 3, 3, 3, 3 }; +constexpr uint8_t T8_ELEMENT_MAXLEVEL[T8_ECLASS_COUNT] = { 255, 30, 29, 29, 21, 21, 21, 18 }; +constexpr uint8_t T8_ELEMENT_MAX_NUM_FACES[T8_ECLASS_COUNT] = { 1, 2, 4, 3, 6, 4, 5, 5 }; +constexpr uint8_t T8_ELEMENT_NUM_CHILDREN[T8_ECLASS_COUNT] = { 1, 2, 4, 4, 8, 8, 8, 10 }; +constexpr uint8_t T8_ELEMENT_NUM_CORNERS[T8_ECLASS_COUNT] = { 1, 2, 4, 3, 8, 4, 6, 5 }; +constexpr uint8_t T8_ELEMENT_NUM_FACES[T8_ECLASS_COUNT] = { 0, 2, 4, 3, 6, 4, 5, 5 }; +constexpr uint8_t T8_ELEMENT_MAX_NUM_FACECHILDREN[T8_ECLASS_COUNT] = { 0, 1, 2, 2, 4, 4, 4, 4 }; + +constexpr uint8_t T8_ELEMENT_NUM_EQUATIONS[T8_ECLASS_COUNT] = { 0, 0, 0, 1, 0, 3, 1, 2 }; + +typedef uint32_t t8_element_coord; +typedef uint8_t t8_element_level; +typedef uint8_t t8_cube_id; +typedef uint8_t t8_child_id; + +template +using t8_element_type = std::bitset; + +template +using t8_element_coords = std::array; + +template +struct t8_standalone_element +{ + /** The refinement level of the element relative to the root at level 0. */ + t8_element_level level; + + /** The coordinates of the anchor vertex of the element. */ + t8_element_coords coords; +}; + +#endif /* T8_STANDALONE_ELEMENTS_HXX */ diff --git a/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx b/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx new file mode 100644 index 0000000000..5c013a2732 --- /dev/null +++ b/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx @@ -0,0 +1,1620 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef T8_STANDALONE_IMPLEMENTATION_HXX +#define T8_STANDALONE_IMPLEMENTATION_HXX + +#include +#include +#include +#include + +template +struct t8_standalone_scheme +{ + public: + /** Constructor + * \param [in] elem_size The size of the elements this scheme holds. + */ + t8_standalone_scheme () + : element_size (sizeof (t8_standalone_element)), scheme_context (sc_mempool_new (element_size)) {}; + + protected: + size_t element_size; /**< The size in bytes of an element of class \a eclass */ + void *scheme_context; /**< Anonymous implementation context. */ + + public: + /** Destructor for all default schemes */ + ~t8_standalone_scheme () + { + T8_ASSERT (scheme_context != NULL); + SC_ASSERT (((sc_mempool_t *) scheme_context)->elem_count == 0); + sc_mempool_destroy ((sc_mempool_t *) scheme_context); + } + + /** Move constructor */ + t8_standalone_scheme (t8_standalone_scheme &&other) noexcept + : element_size (other.element_size), scheme_context (other.scheme_context) + { + other.scheme_context = nullptr; + } + + /** Move assignment operator */ + t8_standalone_scheme & + operator= (t8_standalone_scheme &&other) noexcept + { + if (this != &other) { + // Free existing resources of moved-to object + if (scheme_context) { + sc_mempool_destroy ((sc_mempool_t *) scheme_context); + } + + // Transfer ownership of resources + element_size = other.element_size; + scheme_context = other.scheme_context; + + // Leave the source object in a valid state + other.scheme_context = nullptr; + } + return *this; + } + + /** Copy constructor */ + t8_standalone_scheme (const t8_standalone_scheme &other) + : element_size (other.element_size), scheme_context (sc_mempool_new (other.element_size)) {}; + + /** Copy assignment operator */ + t8_standalone_scheme & + operator= (const t8_standalone_scheme &other) + { + if (this != &other) { + // Free existing resources of assigned-to object + if (scheme_context) { + sc_mempool_destroy ((sc_mempool_t *) scheme_context); + } + + // Copy the values from the source object + element_size = other.element_size; + scheme_context = sc_mempool_new (other.element_size); + } + return *this; + } + + // ################################################____GENERAL INFO____################################################ + + /** Return the tree class of this scheme. + * \return The tree class of this scheme. + */ + constexpr t8_eclass_t + get_eclass (void) const + { + return TEclass; + } + + /** Return the size of any element of a given class. + * \return The size of an element. + */ + static constexpr size_t + get_element_size (void) noexcept + { + return sizeof (t8_standalone_element); + } + + /** Returns true, if there is one element in the tree, that does not refine into 2^dim children. + * Returns false otherwise. + * \return non-zero if there is one element in the tree that does not refine into 2^dim children. + */ + static constexpr int + refines_irregular (void) noexcept + { + if constexpr (TEclass == T8_ECLASS_PYRAMID) { + return 1; + } + return 0; + } + + /** Return the maximum allowed level for any element of a given class. + * \return The maximum allowed level for elements of class \b ts. + */ + static constexpr int + get_maxlevel (void) noexcept + { + return T8_ELEMENT_MAXLEVEL[TEclass]; + } + + // ################################################____SHAPE INFORMATION____################################################ + + /** Compute the number of corners of a given element. + * \param [in] elem The element. + * \return The number of corners of \a elem. + */ + static constexpr int + element_get_num_corners (const t8_element_t *elem) noexcept + { + T8_ASSERT (element_is_valid (elem)); + + return T8_ELEMENT_NUM_CORNERS[TEclass]; + } + + /** Compute the number of faces of a given element. + * \param [in] elem The element. + * \return The number of faces of \a elem. + */ + static constexpr int + element_get_num_faces (const t8_element_t *elem) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + return 0; + } + + /** Compute the maximum number of faces of a given element and all of its + * descendants. + * \param [in] elem The element. + * \return The maximum number of faces of \a elem and its descendants. + */ + static constexpr int + element_get_max_num_faces (const t8_element_t *elem) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + return 0; + } + + /** Return the shape of an allocated element according its type. + * For example, a child of an element can be an element of a different shape + * and has to be handled differently - according to its shape. + * \param [in] elem The element to be considered + * \return The shape of the element as an eclass + */ + static constexpr t8_element_shape_t + element_get_shape (const t8_element_t *elem) noexcept + { + T8_ASSERT (element_is_valid (elem)); + return TEclass; + } + + /** Return the corner number of an element's face corner. + * Example quad: 2 x --- x 3 + * | | + * | | face 1 + * 0 x --- x 1 + * Thus for face = 1 the output is: corner=0 : 1, corner=1: 3 + * + * \param [in] element The element. + * \param [in] face A face index for \a element. + * \param [in] corner A corner index for the face 0 <= \a corner < num_face_corners. + * \return The corner number of the \a corner-th vertex of \a face. + * + * The order in which the corners must be given is determined by the eclass of \a element: + * LINE/QUAD/TRIANGLE: No specific order. + * HEX : In Z-order of the face starting with the lowest corner number. + * TET : Starting with the lowest corner number counterclockwise as seen from + * 'outside' of the element. + */ + static constexpr int + element_get_face_corner (const t8_element_t *element, const int face, const int corner) noexcept + { + SC_ABORT ("This function is not implemented yet.\n"); + return 0; + } + + /** Return the face numbers of the faces sharing an element's corner. + * Example quad: 2 x --- x 3 + * | | + * | | face 1 + * 0 x --- x 1 + * face 2 + * Thus for corner = 1 the output is: face=0 : 2, face=1: 1 + * \param [in] element The element. + * \param [in] corner A corner index for the face. + * \param [in] face A face index for \a corner. + * \return The face number of the \a face-th face at \a corner. + */ + static constexpr int + element_get_corner_face (const t8_element_t *element, const int corner, const int face) noexcept + { + SC_ABORT ("This function is not implemented yet.\n"); + return 0; + } + + /** Compute the shape of the face of an element. + * \param [in] elem The element. + * \param [in] face A face of \a elem. + * \return The element shape of the face. + * I.e. T8_ECLASS_LINE for quads, T8_ECLASS_TRIANGLE for tets + * and depending on the face number either T8_ECLASS_QUAD or + * T8_ECLASS_TRIANGLE for prisms. + */ + static constexpr t8_element_shape_t + element_get_face_shape (const t8_element_t *elem, const int face) noexcept + { + SC_ABORT ("This function is not implemented yet.\n"); + return T8_ECLASS_ZERO; + } + + // ################################################____GENERAL HELPER____################################################ + + /** Copy all entries of \b source to \b dest. \b dest must be an existing + * element. No memory is allocated by this function. + * \param [in] source The element whose entries will be copied to \b dest. + * \param [in,out] dest This element's entries will be overwrite with the + * entries of \b source. + * \note \a source and \a dest may point to the same element. + */ + static constexpr void + element_copy (const t8_element_t *source, t8_element_t *dest) noexcept + { + T8_ASSERT (element_is_valid (source)); + if (source == dest) + return; + memcpy ((t8_standalone_element *) dest, (const t8_standalone_element *) source, + sizeof (t8_standalone_element)); + T8_ASSERT (element_is_valid (dest)); + } + + /** Check if two elements are equal. + * \param [in] elem1 The first element. + * \param [in] elem2 The second element. + * \return true if the elements are equal, false if they are not equal + */ + static constexpr int + element_is_equal (const t8_element_t *elem1, const t8_element_t *elem2) noexcept + { + T8_ASSERT (element_is_valid (elem1)); + T8_ASSERT (element_is_valid (elem2)); + + const t8_standalone_element *el1 = (const t8_standalone_element *) elem1; + const t8_standalone_element *el2 = (const t8_standalone_element *) elem2; + if (el1->level != el2->level) + return 0; + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + if (el1->coords[idim] != el2->coords[idim]) + return 0; + } + /* return el1->type == el2->type; + ToDo-Type */ + return 1; + } + + // ################################################____ACCESSOR____################################################ + + /** Return the level of a particular element. + * \param [in] elem The element whose level should be returned. + * \return The level of \b elem. + */ + static constexpr int + element_get_level (const t8_element_t *elem) noexcept + { + T8_ASSERT (element_is_valid (elem)); + return ((const t8_standalone_element *) elem)->level; + } + + // ################################################____REFINEMENT____################################################ + + /** create the root element + * \param [in,out] elem The element that is filled with the root + */ + static constexpr void + get_root (t8_element_t *elem) noexcept + { + t8_standalone_element *el = (t8_standalone_element *) elem; + el->level = 0; + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + el->coords[idim] = 0; + } + /* el->type = 0; + ToDo-Type */ + return; + } + + /** Compute the parent of a given element \b elem and store it in \b parent. + * \b parent needs to be an existing element. No memory is allocated by this function. + * \b elem and \b parent can point to the same element, then the entries of + * \b elem are overwritten by the ones of its parent. + * \param [in] elem The element whose parent will be computed. + * \param [in,out] parent This element's entries will be overwritten by those + * of \b elem's parent. + * The storage for this element must exist + * and match the element class of the parent. + * For a pyramid, for example, it may be either a + * tetrahedron or a pyramid depending on \b elem's childid. + */ + static constexpr void + element_get_parent (const t8_element_t *elem, t8_element_t *parent) noexcept + { + T8_ASSERT (element_is_valid (elem)); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + t8_standalone_element *parent_elem = (t8_standalone_element *) parent; + + T8_ASSERT (el->level > 0); + + if constexpr (T8_ELEMENT_NUM_EQUATIONS[TEclass]) { + SC_ABORT ("Only implemented for hypercubes.\n"); + } + + const t8_element_coord length = element_get_len ((el->level)); + set_coords_at_level_to_zero (el, parent_elem, length); + + parent_elem->level = el->level - 1; + T8_ASSERT (parent_elem->level >= 0); + + T8_ASSERT (element_is_valid (parent)); + } + + /** Compute the number of siblings of an element. That is the number of + * elements with the same parent (if available). + * \param [in] elem The element. + * \return The number of siblings of \a element. + * Note that this number is >= 1, since we count the element itself as a sibling. + * Note that the number of siblings is 1 for the root element. + */ + static constexpr int + element_get_num_siblings (const t8_element_t *elem) noexcept + { + T8_ASSERT (element_is_valid (elem)); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + if (el->level == 0) + return 1; + T8_ASSERT (0 < el->level && el->level <= T8_ELEMENT_MAXLEVEL[TEclass]); + /* To get the number siblings, we first get the parent and then get the number of children of that parent*/ + if constexpr (refines_irregular ()) { + SC_ABORT ("This function is not implemented yet.\n"); + } + else { + return T8_ELEMENT_NUM_CHILDREN[TEclass]; + } + } + + /** Compute a specific sibling of a given element \b elem and store it in \b sibling. + * \b sibling needs to be an existing element. No memory is allocated by this function. + * \b elem and \b sibling can point to the same element, then the entries of + * \b elem are overwritten by the ones of its sibid-th sibling. + * \param [in] elem The element whose sibling will be computed. + * \param [in] sibid The id of the sibling computed. + * \param [in,out] sibling This element's entries will be overwritten by those + * of \b elem's sibid-th sibling. + * The storage for this element must exist + * and match the element class of the sibling. + */ + static constexpr void + element_get_sibling (const t8_element_t *elem, const int sibid, t8_element_t *sibling) noexcept + { + SC_ABORT ("This function is not implemented yet.\n"); + } + + /** Construct the child element of a given number. + * \param [in] elem This must be a valid element, bigger than maxlevel. + * \param [in] childid The number of the child to construct. + * \param [in,out] child The storage for this element must exist + * and match the element class of the child. + * For a pyramid, for example, it may be either a + * tetrahedron or a pyramid depending on \a childid. + * This can be checked by \a t8_element_child_eclass. + * On output, a valid element. + * It is valid to call this function with elem = child. + * \see t8_element_child_eclass + */ + static constexpr void + element_get_child (const t8_element_t *elem, const int childid, t8_element_t *child) noexcept + { + T8_ASSERT (element_is_valid (elem)); + T8_ASSERT (0 <= childid); + T8_ASSERT (childid < element_get_num_children (elem)); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + t8_standalone_element *c = (t8_standalone_element *) child; + + T8_ASSERT (0 <= childid && childid < T8_ELEMENT_NUM_CHILDREN[TEclass]); + T8_ASSERT (0 <= el->level && el->level <= T8_ELEMENT_MAXLEVEL[TEclass]); + + /* Compute the cube id and shift the coordinates accordingly */ + t8_cube_id cube_id; + if constexpr (T8_ELEMENT_NUM_EQUATIONS[TEclass]) { + SC_ABORT ("Only implemented for hypercubes.\n"); + } + else { + cube_id = childid; + } + + const t8_element_coord length = element_get_len (el->level + 1); + + put_cube_id_at_level (el, c, length, cube_id); + + c->level = el->level + 1; + + T8_ASSERT (element_is_valid (child)); + } + + /** Return the number of children of an element when it is refined. + * \param [in] elem The element whose number of children is returned. + * \return The number of children of \a elem if it is to be refined. + */ + static constexpr int + element_get_num_children (const t8_element_t *elem) noexcept + { + T8_ASSERT (element_is_valid (elem)); + + return T8_ELEMENT_NUM_CHILDREN[TEclass]; + } + + /** Construct all children of a given element. + * \param [in] elem This must be a valid element, bigger than maxlevel. + * \param [in] length The length of the output array \a c must match + * the number of children. + * \param [in,out] c The storage for these \a length elements must exist + * and match the element class in the children's ordering. + * On output, all children are valid. + * It is valid to call this function with elem = c[0]. + * \see t8_element_num_children + * \see t8_element_child_eclass + */ + static constexpr void + element_get_children (const t8_element_t *elem, const int length, t8_element_t *c[]) noexcept + { + T8_ASSERT (element_is_valid (elem)); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + T8_ASSERT (0 <= el->level && el->level <= T8_ELEMENT_MAXLEVEL[TEclass]); + + const int num_children = length; + T8_ASSERT (length == element_get_num_children ((const t8_element_t *) el)); + + for (int ichild = num_children - 1; ichild >= 0; ichild--) { + element_get_child ((const t8_element_t *) el, ichild, c[ichild]); + T8_ASSERT (element_is_valid (c[ichild])); + } + } + + /** Compute the child id of an element. + * \param [in] elem This must be a valid element. + * \return The child id of elem. + */ + static constexpr int + element_get_child_id (const t8_element_t *elem) noexcept + { + T8_ASSERT (element_is_valid (elem)); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + T8_ASSERT (el->level >= 0); + if (el->level == 0) { + return -1; + } + const t8_cube_id cube_id = compute_cubeid (el, el->level); + t8_child_id child_id; + if constexpr (T8_ELEMENT_NUM_EQUATIONS[TEclass]) { + SC_ABORT ("Only implemented for hypercubes.\n"); + } + else { + child_id = cube_id; + } + return child_id; + } + + /** Compute the ancestor id of an element, that is the child id + * at a given level. + * \param [in] elem This must be a valid element. + * \param [in] level A refinement level. Must satisfy \a level < elem.level + * \return The child_id of \a elem in regard to its \a level ancestor. + */ + static constexpr int + element_get_ancestor_id (const t8_element_t *elem, const t8_element_level level) noexcept + { + T8_ASSERT (element_is_valid (elem)); + T8_ASSERT (0 <= level && level <= T8_ELEMENT_MAXLEVEL[TEclass]); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + t8_standalone_element ancestor; + T8_ASSERT (0 <= el->level && el->level <= T8_ELEMENT_MAXLEVEL[TEclass]); + + element_get_ancestor (el, level, &ancestor); + return element_get_child_id ((const t8_element_t *) &ancestor); + } + + /** Query whether a given set of elements is a family or not. + * \param [in] fam An array of as many elements as an element of class + * \b ts has siblings. + * \return Zero if \b fam is not a family, nonzero if it is. + * \note level 0 elements do not form a family. + */ + static constexpr int + elements_are_family (t8_element_t *const *fam) noexcept + { +#if T8_ENABLE_DEBUG + const int num_siblings = element_get_num_siblings (fam[0]); + for (int isib = 0; isib < num_siblings; isib++) { + T8_ASSERT (element_is_valid (fam[isib])); + } +#endif + + t8_standalone_element parent, compare; + /* Take the parent of the first element as baseline to compare against */ + element_get_parent ((const t8_element_t *) fam[0], (t8_element_t *) &parent); + const int num_children = element_get_num_children ((const t8_element_t *) &parent); + for (int childid = 0; childid < num_children; childid++) { + /* check whether each element has the same parent */ + element_get_parent ((const t8_element_t *) fam[childid], (t8_element_t *) &compare); + if (element_compare ((const t8_element_t *) &parent, (const t8_element_t *) &compare)) { + return 0; + } + + /* check whether each element is the correct child of the collective parent */ + /* Could be replaced by type comparison as level is already checked in parent comparison */ + element_get_child ((const t8_element_t *) &parent, childid, (t8_element_t *) &compare); + + if (element_compare ((const t8_element_t *) fam[childid], (const t8_element_t *) &compare)) { + return 0; + } + } + return 1; + } + + /** Compute the nearest common ancestor of two elements. That is, + * the element with highest level that still has both given elements as + * descendants. + * \param [in] elem1 The first of the two input elements. + * \param [in] elem2 The second of the two input elements. + * \param [in,out] nca The storage for this element must exist + * and match the element class of the child. + * On output the unique nearest common ancestor of + * \b elem1 and \b elem2. + */ + static constexpr void + element_get_nca (const t8_element_t *elem1, const t8_element_t *elem2, t8_element_t *nca) noexcept + { + T8_ASSERT (element_is_valid (elem1)); + T8_ASSERT (element_is_valid (elem2)); + + const t8_standalone_element *el1 = (const t8_standalone_element *) elem1; + const t8_standalone_element *el2 = (const t8_standalone_element *) elem2; + /* get the first possible level of the nca*/ + int cube_ancestor_level = element_get_cube_nca_level (el1, el2); + int real_level; + if constexpr (T8_ELEMENT_NUM_EQUATIONS[TEclass]) { + SC_ABORT ("Only implemented for hypercubes.\n"); + } + else { + real_level = cube_ancestor_level; + } + /* get the ancestor at the calculated level*/ + element_get_ancestor (el1, real_level, (t8_standalone_element *) nca); + T8_ASSERT (element_is_valid (nca)); + } + + /** Compute the first descendant of a given element. + * \param [in] elem The element whose descendant is computed. + * \param [out] desc The first element in a uniform refinement of \a elem + * of the given level. + * \param [in] level The level, at which the descendant is computed. + */ + static constexpr void + element_get_first_descendant (const t8_element_t *elem, t8_element_t *desc, const t8_element_level level) noexcept + { + T8_ASSERT (element_is_valid (elem)); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + t8_standalone_element *d = (t8_standalone_element *) desc; + + T8_ASSERT (level >= el->level); + T8_ASSERT (0 <= level && level <= T8_ELEMENT_MAXLEVEL[TEclass]); + + /* The first descendant of an element has the same anchor coords and type, but another level */ + element_copy ((const t8_element_t *) el, (t8_element_t *) d); + d->level = level; + + T8_ASSERT (element_is_valid ((t8_element_t *) d)); + } + + /** Compute the last descendant of a given element. + * \param [in] elem The element whose descendant is computed. + * \param [out] desc The last element in a uniform refinement of \a elem + * of the given level. + * \param [in] level The level, at which the descendant is computed. + */ + static constexpr void + element_get_last_descendant (const t8_element_t *elem, t8_element_t *desc, const t8_element_level level) noexcept + { + T8_ASSERT (element_is_valid (elem)); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + t8_standalone_element *d = (t8_standalone_element *) desc; + + T8_ASSERT (level >= el->level); + T8_ASSERT (0 <= level && level <= T8_ELEMENT_MAXLEVEL[TEclass]); + + element_copy ((const t8_element_t *) el, (t8_element_t *) d); + d->level = level; + + /* Shift the coords to the eighth cube. The type of the last descendant + * is the type of the input element */ + t8_element_coord coord_offset = element_get_len (el->level) - element_get_len (level); + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + d->coords[idim] |= coord_offset; + } + + T8_ASSERT (element_is_valid (desc)); + } + + // ################################################____FACE REFINEMENT____################################################ + + /** Return the number of children of an element's face when the element is refined. + * \param [in] elem The element whose face is considered. + * \param [in] face A face of \a elem. + * \return The number of children of \a face if \a elem is to be refined. + */ + static constexpr int + element_get_num_face_children (const t8_element_t *elem, const int face) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + return 0; + } + + /** Given an element and a face of the element, compute all children of + * the element that touch the face. + * \param [in] elem The element. + * \param [in] face A face of \a elem. + * \param [in,out] children Allocated elements, in which the children of \a elem + * that share a face with \a face are stored. + * They will be stored in order of their linear id. + * \param [in] num_children The number of elements in \a children. Must match + * the number of children that touch \a face. + * \ref t8_element_num_face_children + * \param [in,out] child_indices If not NULL, an array of num_children integers must be given, + * on output its i-th entry is the child_id of the i-th face_child. + * It is valid to call this function with elem = children[0]. + */ + static constexpr void + element_get_children_at_face (const t8_element_t *elem, int face, t8_element_t *children[], const int num_children, + int *child_indices) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + } + + /** Given a face of an element and a child number of a child of that face, return the face number + * of the child of the element that matches the child face. + * \verbatim + x ---- x x x x ---- x + | | | | | | | <-- f + | | | x | x--x + | | | | | + x ---- x x x ---- x + elem face face_child Returns the face number f + \endverbatim + + * \param [in] elem The element. + * \param [in] face Then number of the face. + * \param [in] face_child A number 0 <= \a face_child < num_face_children, + * specifying a child of \a elem that shares a face with \a face. + * These children are counted in linear order. This coincides with + * the order of children from a call to \ref t8_element_children_at_face. + * \return The face number of the face of a child of \a elem + * that coincides with \a face_child. + */ + static constexpr int + element_face_get_child_face (const t8_element_t *elem, const int face, const int face_child) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + return 0; + } + + /** Given a face of an element return the face number + * of the parent of the element that matches the element's face. Or return -1 if + * no face of the parent matches the face. + + * \param [in] elem The element. + * \param [in] face Then number of the face. + * \return If \a face of \a elem is also a face of \a elem's parent, + * the face number of this face. Otherwise -1. + * \note For the root element this function always returns \a face. + */ + static constexpr int + element_face_get_parent_face (const t8_element_t *elem, const int face) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + return 0; + } + + /** Construct the first descendant of an element at a given level that touches a given face. + * \param [in] elem The input element. + * \param [in] face A face of \a elem. + * \param [in, out] first_desc An allocated element. This element's data will be + * filled with the data of the first descendant of \a elem + * that shares a face with \a face. + * \param [in] level The level, at which the first descendant is constructed + */ + static constexpr void + element_get_first_descendant_face (const t8_element_t *elem, const int face, t8_element_t *first_desc, + const t8_element_level level) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + } + + /** Construct the last descendant of an element at a given level that touches a given face. + * \param [in] elem The input element. + * \param [in] face A face of \a elem. + * \param [in, out] last_desc An allocated element. This element's data will be + * filled with the data of the last descendant of \a elem + * that shares a face with \a face. + * \param [in] level The level, at which the last descendant is constructed + */ + static constexpr void + element_get_last_descendant_face (const t8_element_t *elem, const int face, t8_element_t *last_desc, + const t8_element_level level) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + } + + // ################################################____FACE NEIGHBOR____################################################ + + /** Compute whether a given element shares a given face with its root tree. + * \param [in] elem The input element. + * \param [in] face A face of \a elem. + * \return True if \a face is a subface of the element's root element. + * \note You can compute the corresponding face number of the tree via \ref t8_element_tree_face. + */ + static constexpr int + element_is_root_boundary (const t8_element_t *elem, const int face) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + return 0; + } + + /** Given an element and a face of this element. If the face lies on the + * tree boundary, return the face number of the tree face. + * If not the return value is arbitrary. + * You can call \ref t8_element_is_root_boundary to query whether the face is + * at the tree boundary. + * \param [in] elem The element. + * \param [in] face The index of a face of \a elem. + * \return The index of the tree face that \a face is a subface of, if + * \a face is on a tree boundary. + * Any arbitrary integer if \a is not at a tree boundary. + * \warning The return value may look like a valid face of the tree even if + * the element does not lie on the root boundary. + */ + static constexpr int + element_get_tree_face (const t8_element_t *elem, const int face) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + return 0; + } + + /** Construct the face neighbor of a given element if this face neighbor + * is inside the root tree. Return 0 otherwise. + * \param [in] elem The element to be considered. + * \param [in,out] neigh If the face neighbor of \a elem along \a face is inside + * the root tree, this element's data is filled with the + * data of the face neighbor. Otherwise the data can be modified + * arbitrarily. + * \param [in] face The number of the face along which the neighbor should be + * constructed. + * \param [out] neigh_face The number of \a face as viewed from \a neigh. + * An arbitrary value, if the neighbor is not inside the root tree. + * \return True if \a neigh is inside the root tree. + * False if not. In this case \a neigh's data can be arbitrary + * on output. + */ + static constexpr int + element_get_face_neighbor_inside (const t8_element_t *elem, t8_element_t *neigh, const int face, + int *neigh_face) noexcept + { + SC_ABORT ("This function is not implemented in this scheme yet.\n"); + return 0; + } + + // ################################################____TREE FACE TRANSFORMATION____################################################ */ + + /** Suppose we have two trees that share a common face f. + * Given an element e that is a subface of f in one of the trees + * and given the orientation of the tree connection, construct the face + * element of the respective tree neighbor that logically coincides with e + * but lies in the coordinate system of the neighbor tree. + * \param [in] elem1 The face element. + * \param [in,out] elem2 On return the face element \a elem1 with respective + * to the coordinate system of the other tree. + * \param [in] orientation The orientation of the tree-tree connection. + * \see t8_cmesh_set_join + * \param [in] sign Depending on the topological orientation of the two tree faces, + * either 0 (both faces have opposite orientation) + * or 1 (both faces have the same top. orientation). + * \ref t8_eclass_face_orientation + * \param [in] is_smaller_face Flag to declare whether \a elem1 belongs to + * the smaller face. A face f of tree T is smaller than + * f' of T' if either the eclass of T is smaller or if + * the classes are equal and f *el = (t8_standalone_element *) elem; + + get_root ((t8_element_t *) el); + + /* There is only one element at level 0, so it must be root */ + if (level == 0) { + T8_ASSERT (id == 0); + return; + } + + T8_ASSERT (0 <= id); + T8_ASSERT (1 <= level && level <= T8_ELEMENT_MAXLEVEL[TEclass]); + t8_standalone_element child; + + while (el->level < level) { + /* Shortcut if we need the first descendant of the subtree*/ + if (id == 0) { + element_get_first_descendant ((const t8_element_t *) el, (t8_element_t *) el, level); + return; + } + + t8_linearidx_t sum_descendants_of_children_before; + t8_linearidx_t sum_descendants_of_children_until_current = 0; + int childindex = -1; + + /* Find the first child id so that the sum of descendants of previous child and the own number of descendants is greater than id */ + do { + /* Go to the next child */ + sum_descendants_of_children_before = sum_descendants_of_children_until_current; + childindex++; + T8_ASSERT (childindex < element_get_num_children ((const t8_element_t *) el)); + + element_get_num_children ((const t8_element_t *) el); + + element_get_child ((const t8_element_t *) el, childindex, (t8_element_t *) &child); + const t8_linearidx_t num_descendants_of_child = element_count_leaves ((t8_element_t *) &child, level); + + /* Add number of descendant of current child to cumulative sum */ + sum_descendants_of_children_until_current = sum_descendants_of_children_before + num_descendants_of_child; + + } while (sum_descendants_of_children_until_current <= id); + + /* Replace el by child to go into next iteration at finer level*/ + element_get_child ((const t8_element_t *) el, childindex, (t8_element_t *) el); + /* get id in subtree of child */ + id -= sum_descendants_of_children_before; + } + T8_ASSERT (id == 0); + return; + } + + /** Compute the linear id of a given element in a hypothetical uniform + * refinement of a given level. + * \param [in] elem The element whose id we compute. + * \param [in] level The level of the uniform refinement to consider. + * \return The linear id of the element. + */ + static constexpr t8_linearidx_t + element_get_linear_id (const t8_element_t *elem, const t8_element_level level) noexcept + { + T8_ASSERT (element_is_valid (elem)); + const t8_standalone_element *el = (const t8_standalone_element *) elem; + t8_standalone_element ancestor; + + /* Determine the starting element for the iterative linear id computation. */ + if (level < el->level) { + /* Throw away child ids up to the coarser level */ + element_get_ancestor (el, level, &ancestor); + } + else { + /* Start with the input element. + Copy to have a mutable element. */ + element_copy ((const t8_element_t *) el, (t8_element_t *) &ancestor); + } + + t8_linearidx_t id = 0; + t8_standalone_element child; + + while (ancestor.level != 0) { + const t8_child_id childid = element_get_child_id ((t8_element_t *) &ancestor); + element_get_parent ((t8_element_t *) &ancestor, (t8_element_t *) &ancestor); + t8_linearidx_t parent_id = 0; + + for (int ichild = 0; ichild < childid; ichild++) { + /* el is now parent, so compute child to get sibling of previous el */ + + element_get_child ((const t8_element_t *) &ancestor, ichild, (t8_element_t *) &child); + const t8_linearidx_t num_child_descendants = element_count_leaves ((t8_element_t *) &child, level); + parent_id += num_child_descendants; + } + id += parent_id; + } + T8_ASSERT (id >= 0); + return id; + } + + /** Construct the successor in a uniform refinement of a given element. + * \param [in] elem1 The element whose successor should be constructed. + * \param [in,out] elem2 The element whose entries will be set. + */ + static constexpr void + element_construct_successor (const t8_element_t *elem1, t8_element_t *elem2) noexcept + { + T8_ASSERT (element_is_valid (elem1)); + + const t8_standalone_element *elem = (const t8_standalone_element *) elem1; + t8_standalone_element *succ = (t8_standalone_element *) elem2; + + element_copy ((const t8_element_t *) elem, (t8_element_t *) succ); + + const t8_child_id child_id = element_get_child_id ((const t8_element_t *) elem); + const int num_siblings = element_get_num_siblings ((const t8_element_t *) elem); + T8_ASSERT (0 <= child_id && child_id < num_siblings); + /* If the element is the last child of the parent, we need to go to the parent's successor (go to a coarser level)*/ + if (child_id == num_siblings - 1) { + element_get_parent ((const t8_element_t *) succ, (t8_element_t *) succ); + element_construct_successor ((const t8_element_t *) succ, (t8_element_t *) succ); + element_get_child ((const t8_element_t *) succ, 0, (t8_element_t *) succ); + } + else { + element_get_parent ((const t8_element_t *) succ, (t8_element_t *) succ); + element_get_child ((const t8_element_t *) succ, child_id + 1, (t8_element_t *) succ); + } + + T8_ASSERT (element_is_valid (elem2)); + } + + /** Count how many leaf descendants of a given uniform level an element would produce. + * \param [in] t The element to be checked. + * \param [in] level A refinement level. + * \return Suppose \a t is uniformly refined up to level \a level. The return value + * is the resulting number of elements (of the given level). + * If \a level < t8_element_level(t), the return value should be 0. + * + * Example: If \a t is a line element that refines into 2 line elements on each level, + * then the return value is max(0, 2^{\a level - level(\a t)}). + * Thus, if \a t's level is 0, and \a level = 3, the return value is 2^3 = 8. + */ + static constexpr t8_gloidx_t + element_count_leaves (const t8_element_t *elem, const t8_element_level level) noexcept + { + T8_ASSERT (element_is_valid (elem)); + T8_ASSERT (0 <= level && level <= T8_ELEMENT_MAXLEVEL[TEclass]); + if (level < element_get_level (elem)) { + return 0; + } + else { + return num_descendants_at_leveldiff (elem, level - element_get_level (elem)); + } + } + + /** Count how many leaf descendants of a given uniform level the root element will produce. + * \param [in] level A refinement level. + * \return The value of \ref t8_element_count_leaves if the input element + * is the root (level 0) element. + * + * This is a convenience function, and can be implemented via + * \ref t8_element_count_leaves. + */ + static constexpr t8_gloidx_t + count_leaves_from_root (const t8_element_level level) noexcept + { + T8_ASSERT (level <= T8_ELEMENT_MAXLEVEL[TEclass]); + T8_ASSERT (level >= 0); + if constexpr (TEclass == T8_ECLASS_PYRAMID) { + SC_ABORT ("Not implemented yet.\n"); + } + return 1LL << (level * T8_ELEMENT_DIM[TEclass]); + } + + /** Compare two elements. + * \param [in] elem1 The first element. + * \param [in] elem2 The second element. + * \return negative if elem1 < elem2, zero if elem1 equals elem2 + * and positive if elem1 > elem2. + * If elem2 is a copy of elem1 then the elements are equal. + */ + static constexpr int + element_compare (const t8_element_t *elem1, const t8_element_t *elem2) noexcept + { + T8_ASSERT (element_is_valid (elem1)); + T8_ASSERT (element_is_valid (elem2)); + + const t8_standalone_element *e1 = (const t8_standalone_element *) elem1; + const t8_standalone_element *e2 = (const t8_standalone_element *) elem2; + + const int maxlvl = SC_MAX (e1->level, e2->level); + + const t8_linearidx_t id1 = element_get_linear_id ((const t8_element_t *) e1, maxlvl); + const t8_linearidx_t id2 = element_get_linear_id ((const t8_element_t *) e2, maxlvl); + if (id1 == id2) { + if (e1->level == e2->level) { + return 0; + } + else { + return e1->level - e2->level; + } + } + return id1 < id2 ? -1 : 1; + } + + // ################################################____VISUALIZATION____################################################ + + /** Compute the coordinates of a given element vertex inside a reference tree + * that is embedded into [0,1]^d (d = dimension). + * \param [in] elem The element to be considered. + * \param [in] vertex The id of the vertex whose coordinates shall be computed. + * \param [out] coords An array of at least as many doubles as the element's dimension + * whose entries will be filled with the coordinates of \a vertex. + */ + static constexpr void + element_get_vertex_reference_coords (const t8_element_t *elem, const int vertex, double coords[]) noexcept + { + T8_ASSERT (element_is_valid (elem)); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + if constexpr (TEclass == T8_ECLASS_VERTEX) { + return; + } + else { + int coords_int[T8_ELEMENT_DIM[TEclass]]; + T8_ASSERT (0 <= vertex && vertex < T8_ELEMENT_NUM_CORNERS[TEclass]); + element_compute_coords (el, vertex, coords_int); + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + coords[idim] = coords_int[idim] / (double) get_root_len (); + } + } + } + + /** Convert a point in the reference space of an element to a point in the + * reference space of the tree. + * + * \param [in] elem The element. + * \param [in] coords_input The coordinates of the point in the reference space of the element. + * \param [in] user_data User data. + * \param [out] out_coords The coordinates of the point in the reference space of the tree. + */ + static constexpr void + element_get_reference_coords (const t8_element_t *elem, const double *ref_coords, const size_t num_coords, + double *out_coords) noexcept + { + double *current_ref_coords = (double *) ref_coords; + double *current_out_coords = out_coords; + t8_element_coord length = element_get_len (element_get_level (elem)); + + for (size_t coord = 0; coord < num_coords; ++coord) { + for (int dim = 0; dim < T8_ELEMENT_DIM[TEclass]; ++dim) { + current_out_coords[dim] + = ((t8_standalone_element *) elem)->coords[dim] + current_ref_coords[dim] * length; + + current_out_coords[dim] /= (double) get_root_len (); + } + + current_ref_coords += T8_ECLASS_MAX_DIM; + current_out_coords += T8_ELEMENT_DIM[TEclass]; + } + } + + // ################################################____MEMORY____################################################ + + /** Allocate memory for an array of elements of a given class and initialize them. + * \param [in] length The number of elements to be allocated. + * \param [in,out] elems On input an array of \b length many unallocated + * element pointers. + * On output all these pointers will point to an allocated + * and initialized element. + * \note Not every element that is created in t8code will be created by a call + * to this function. However, if an element is not created using \ref t8_element_new, + * then it is guaranteed that \ref t8_element_init is called on it. + * \note In debugging mode, an element that was created with \ref t8_element_new + * must pass \ref t8_element_is_valid. + * \note If an element was created by \ref t8_element_new then \ref t8_element_init + * may not be called for it. Thus, \ref t8_element_new should initialize an element + * in the same way as a call to \ref t8_element_init would. + * \see t8_element_init + * \see t8_element_is_valid + */ + /* TODO: would it be better to directly allocate an array of elements, + * not element pointers? */ + void + element_new (const int length, t8_element_t **elem) const noexcept + { + /* allocate memory */ + T8_ASSERT (this->scheme_context != NULL); + T8_ASSERT (0 <= length); + T8_ASSERT (elem != NULL); + + for (int i = 0; i < length; ++i) { + elem[i] = (t8_element_t *) sc_mempool_alloc ((sc_mempool_t *) this->scheme_context); + } + +/* in debug mode, set sensible default values. */ +#ifdef T8_ENABLE_DEBUG + { + int i; + for (i = 0; i < length; i++) { + element_init (1, elem[i]); + } + } +#endif + } + + /** Initialize an array of allocated elements. + * \param [in] length The number of elements to be initialized. + * \param [in,out] elems On input an array of \b length many allocated + * elements. + * \note In debugging mode, an element that was passed to \ref t8_element_init + * must pass \ref t8_element_is_valid. + * \note If an element was created by \ref t8_element_new then \ref t8_element_init + * may not be called for it. Thus, \ref t8_element_new should initialize an element + * in the same way as a call to \ref t8_element_init would. + * \see t8_element_new + * \see t8_element_is_valid + */ + static inline void + element_init (const int length, t8_element_t *elem) noexcept + { +#ifdef T8_ENABLE_DEBUG + int ielem; + t8_standalone_element *el = (t8_standalone_element *) elem; + /* Set all values to 0 */ + for (ielem = 0; ielem < length; ielem++) { + element_set_linear_id ((t8_element_t *) (el + ielem), 0, 0); + T8_ASSERT (element_is_valid ((t8_element_t *) (el + ielem))); + } +#endif + } + + /** Deinitialize an array of allocated elements. + * \param [in] length The number of elements to be deinitialized. + * \param [in,out] elems On input an array of \a length many allocated + * and initialized elements, on output an array of + * \a length many allocated, but not initialized elements. + * \note Call this function if you called t8_element_init on the element pointers. + * \see t8_element_init + */ + static constexpr void + element_deinit (const int length, t8_element_t *elem) noexcept + { + } + + /** Deallocate an array of elements. + * \param [in] length The number of elements in the array. + * \param [in,out] elems On input an array of \b length many allocated + * element pointers. + * On output all these pointers will be freed. + * \b elem itself will not be freed by this function. + */ + void + element_destroy (const int length, t8_element_t **elem) const noexcept + { + T8_ASSERT (this->scheme_context != NULL); + T8_ASSERT (0 <= length); + T8_ASSERT (elem != NULL); + for (int i = 0; i < length; ++i) { + sc_mempool_free ((sc_mempool_t *) scheme_context, elem[i]); + } + } + + // ################################################____DEBUG____################################################ + +#ifdef T8_ENABLE_DEBUG + /** Query whether a given element can be considered as 'valid' and it is + * safe to perform any of the above algorithms on it. + * For example this could mean that all coordinates are in valid ranges + * and other membervariables do have meaningful values. + * \param [in] elem The element to be checked. + * \return True if \a elem is safe to use. False otherwise. + * \note An element that is constructed with \ref t8_element_new + * must pass this test. + * \note An element for which \ref t8_element_init was called must pass + * this test. + * \note This function is used for debugging to catch certain errors. + * These can for example occur when an element points to a region + * of memory which should not be interpreted as an element. + * \note We recommend to use the assertion T8_ASSERT (element_is_valid (elem)) + * in the implementation of each of the functions in this file. + */ + static constexpr int + element_is_valid (const t8_element_t *elem) noexcept + { + T8_ASSERT (elem != NULL); + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + const t8_element_coord max_coord = get_root_len () - 1; + + /* Check the level */ + int is_valid = 0 <= el->level && el->level <= T8_ELEMENT_MAXLEVEL[TEclass]; + /* Check coordinates, we allow a boundary layer around the root-element */ + for (int i = 0; i < T8_ELEMENT_DIM[TEclass]; i++) { + is_valid = is_valid && -(int64_t) get_root_len () <= el->coords[i] && el->coords[i] <= max_coord; + } + + return is_valid; + } + + /** + * Print a given element. For a example for a triangle print the coordinates + * and the level of the triangle. This function is only available in the + * debugging configuration. + * + * \param [in] elem The element to print + */ + static constexpr void + element_debug_print (const t8_element_t *elem) noexcept + { + + const t8_standalone_element *el = (const t8_standalone_element *) elem; + + t8_debugf ("level: %i\n", el->level); + for (int i = 0; i < T8_ELEMENT_DIM[TEclass]; i++) { + t8_debugf ("x_%i: %i \n", i, el->coords[i]); + } + /** for (int e = 0; e < T8_ELEMENT_NUM_EQUATIONS[TEclass]; e++) { + * t8_debugf ("t_%i: %i \n", e, el->type[e]); + *} + * ToDo-Type */ + } + + static constexpr void + element_to_string (const t8_element_t *elem, char *debug_string, const int string_size) noexcept + { + const t8_standalone_element *el = (const t8_standalone_element *) elem; + int offset = 0; + offset += snprintf (debug_string + offset, string_size - offset, "level: %i\n", el->level); + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + offset += snprintf (debug_string + offset, string_size - offset, "x_%i: %i \n", idim, el->coords[idim]); + } + } + +#endif + + // ################################################____MPI____################################################ + + /** Pack multiple elements into contiguous memory, so they can be sent via MPI. + * \param [in] elements Array of elements that are to be packed + * \param [in] count Number of elements to pack + * \param [in,out] send_buffer Buffer in which to pack the elements + * \param [in] buffer_size size of the buffer (in order to check that we don't access out of range) + * \param [in, out] position the position of the first byte that is not already packed + * \param [in] comm MPI Communicator + */ + constexpr void + element_MPI_Pack (t8_element_t **const elements, const unsigned int count, void *send_buffer, const int buffer_size, + int *position, sc_MPI_Comm comm) const noexcept + + { + int mpiret; + t8_standalone_element **els = (t8_standalone_element **) elements; + + for (unsigned int ielem = 0; ielem < count; ielem++) { + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + mpiret = sc_MPI_Pack (&(els[ielem]->coords[idim]), 1, sc_MPI_INT, send_buffer, buffer_size, position, comm); + SC_CHECK_MPI (mpiret); + } + mpiret = sc_MPI_Pack (&els[ielem]->level, 1, sc_MPI_INT8_T, send_buffer, buffer_size, position, comm); + SC_CHECK_MPI (mpiret); + } + } + + /** Determine an upper bound for the size of the packed message of \a count elements + * \param [in] count Number of elements to pack + * \param [in] comm MPI Communicator + * \param [out] pack_size upper bound on the message size + */ + constexpr void + element_MPI_Pack_size (const unsigned int count, sc_MPI_Comm comm, int *pack_size) const noexcept + { + int singlesize = 0; + int datasize = 0; + int mpiret; + + /* x,y,z */ + mpiret = sc_MPI_Pack_size (1, sc_MPI_INT, comm, &datasize); + SC_CHECK_MPI (mpiret); + singlesize += T8_ELEMENT_DIM[TEclass] * datasize; + + /* level */ + mpiret = sc_MPI_Pack_size (1, sc_MPI_INT8_T, comm, &datasize); + SC_CHECK_MPI (mpiret); + singlesize += datasize; + + *pack_size = count * singlesize; + } + + /** Unpack multiple elements from contiguous memory that was received via MPI. + * \param [in] recvbuf Buffer from which to unpack the elements + * \param [in] buffer_size size of the buffer (in order to check that we don't access out of range) + * \param [in, out] position the position of the first byte that is not already packed + * \param [in] elements Array of initialised elements that is to be filled from the message + * \param [in] count Number of elements to unpack + * \param [in] comm MPI Communicator + */ + constexpr void + element_MPI_Unpack (void *recvbuf, const int buffer_size, int *position, t8_element_t **elements, + const unsigned int count, sc_MPI_Comm comm) const noexcept + { + int mpiret; + t8_standalone_element **els = (t8_standalone_element **) elements; + + for (unsigned int ielem = 0; ielem < count; ielem++) { + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + mpiret = sc_MPI_Unpack (recvbuf, buffer_size, position, &(els[ielem]->coords[idim]), 1, sc_MPI_INT, comm); + SC_CHECK_MPI (mpiret); + } + mpiret = sc_MPI_Unpack (recvbuf, buffer_size, position, &(els[ielem]->level), 1, sc_MPI_INT8_T, comm); + SC_CHECK_MPI (mpiret); + } + } + + private: + // ################################################____HELPER____################################################ + + /** The length of a element at a given level in integer coordinates + * \param[in] level Level of the element + */ + static constexpr t8_element_coord + element_get_len (const t8_element_level level) noexcept + { + return 1 << (T8_ELEMENT_MAXLEVEL[TEclass] - (level)); + } + + /** Compute the cube id of an element + * \param[in] elem Input element + * \param[in] level The refinement level + */ + static constexpr t8_cube_id + compute_cubeid (const t8_standalone_element *elem, const t8_element_level level) noexcept + { + t8_cube_id cube_id = 0; + + T8_ASSERT (0 <= elem->level && elem->level <= T8_ELEMENT_MAXLEVEL[TEclass]); + const t8_element_coord h = element_get_len (level); + + /* The cube id of the root element is 0.*/ + if (level != 0) { + for (int i = 0; i < T8_ELEMENT_DIM[TEclass]; i++) { + cube_id |= ((elem->coords[i] & h) ? 1 << i : 0); + } + } + return cube_id; + } + + /** + * Compute the ancestor of \a el at a given level via the equation properties + * + * \param[in] elem Input element + * \param[in] level Level of the ancestor to compute + * \param[in, out] ancestor Allocated element that will be filled with the data of the ancestor. + */ + static constexpr void + element_get_ancestor (const t8_standalone_element *elem, const t8_element_level level, + t8_standalone_element *ancestor) noexcept + { + T8_ASSERT (0 <= level && level <= elem->level); + if (elem != ancestor) { + element_copy ((const t8_element_t *) elem, (t8_element_t *) ancestor); + } + if (elem->level == level) { + return; + } + + /* Set type */ + if constexpr (T8_ELEMENT_NUM_EQUATIONS[TEclass]) { + SC_ABORT ("Only implemented for hypercubes.\n"); + } + + /* The coordinates and the type of the ancestor are defined by the level. */ + element_cut_coordinates (ancestor, T8_ELEMENT_MAXLEVEL[TEclass] - level); + + ancestor->level = level; + } + + /** Use the number of zero bits on the left to detrime the level of the nearest common ancestor of two elements. + * \param[in] elem1 First input element + * \param[in] elem2 Second input element + * \return The level of the nearest common ancestor of the two elements + */ + static constexpr t8_element_level + element_get_cube_nca_level (const t8_standalone_element *elem1, + const t8_standalone_element *elem2) noexcept + { + /* XOR all coordinates. The number of zeros on the left determines the level needed, so that the coordinates equal. + OR over all these bit representations. The number of zeros on the left in this new number equals the coarses of all of these levels. + Therefore this is the level needed so that all coordinates equal.*/ + t8_element_coord maxexclor = 0; + + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + maxexclor |= (elem1->coords[idim] ^ elem2->coords[idim]); + } + + const int num_zeros = number_of_leading_zeros (maxexclor); + /* If one element already is the ancestor of the other element num_zeros evaluates to maxlevel, in that case return the coarser of both levels*/ + return SC_MIN (num_zeros, (int) SC_MIN (elem1->level, elem2->level)); + } + + /** Compute the number of zero bits on the left side of coords. + * \param[in] coordinates Input coordinates + * \return Number of leading zeros + */ + static constexpr int + number_of_leading_zeros (const t8_element_coord coordinates) noexcept + { + const int num_of_active_bits_used = SC_LOG2_32 (coordinates) + 1; + T8_ASSERT (num_of_active_bits_used <= T8_ELEMENT_MAXLEVEL[TEclass]); + + return T8_ELEMENT_MAXLEVEL[TEclass] - num_of_active_bits_used; + } + + /** + * Set the \a shift last bits of every coordinate to zero. + * + * \param[in, out] elem Input element + * \param[in] shift Number of bits to set to zero + */ + static constexpr void + element_cut_coordinates (t8_standalone_element *elem, const int shift) noexcept + { + T8_ASSERT (0 <= shift && shift <= T8_ELEMENT_MAXLEVEL[TEclass]); + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + elem->coords[idim] = (elem->coords[idim] >> shift) << shift; + } + } + + /** + * Set the least significant coordinates bits to zero. + * + * \param[in] elem Input element + * \param[in, out] parent_elem Parent element + * \param[in] length int that is 1 at the level of the input element + * Note length is used as additional input to avoid recomputation. + */ + static constexpr void + set_coords_at_level_to_zero (const t8_standalone_element *elem, t8_standalone_element *parent_elem, + const t8_element_coord length) noexcept + { + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + parent_elem->coords[idim] = elem->coords[idim] & ~length; + } + } + + /** + * Adjust the coordinates based on the cube ID. + * + * \param[in] parent Input element + * \param[in, out] child Output element + * \param[in] length int that is 1 at the level of the child element + * \param[in] cube_id Cube ID for bitwise operation + * Note length is used as additional input to avoid recomputation. + */ + static constexpr void + put_cube_id_at_level (const t8_standalone_element *parent, t8_standalone_element *child, + const t8_element_coord length, const t8_cube_id cube_id) noexcept + { + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + child->coords[idim] = parent->coords[idim] + ((cube_id & (1 << idim)) ? length : 0); + } + } + + /** Compute the length of the root element of the current TEclass. The length for a Vertex root element is always 0. + * \return The length of the root element + */ + + static constexpr t8_element_coord + get_root_len () noexcept + { + if constexpr (TEclass == T8_ECLASS_VERTEX) { + return 0; + } + else { + return 1 << T8_ELEMENT_MAXLEVEL[TEclass]; + } + } + + /** Get the number of descendants of an element at a given leveldiff. + * \param[in] elem Input element + * \param[in] leveldiff Difference between the level of the element + * \return number of descendants + * Note Caller is responsible for taking the absolute value of leveldiff + */ + static constexpr t8_linearidx_t + num_descendants_at_leveldiff (const t8_element_t *elem, const t8_element_level leveldiff) noexcept + { + if (leveldiff < 0) + return 0; + if constexpr (TEclass == T8_ECLASS_PYRAMID) { + SC_ABORT ("Not implemented yet.\n"); + } + return 1LL << (T8_ELEMENT_DIM[TEclass] * leveldiff); + } + + /** Compute the coordinates of a vertex of an element. + * \param [in] elem Input element. + * \param [in] vertex The number of the vertex. + * \param [out] coords An array of 3 t8_element_coord that + * will be filled with the coordinates of the vertex. + */ + static constexpr void + element_compute_coords (const t8_standalone_element *elem, const int vertex, int coords[]) noexcept + { + T8_ASSERT (0 <= vertex && vertex < element_get_num_corners ((const t8_element_t *) elem)); + + if constexpr (T8_ELEMENT_NUM_EQUATIONS[TEclass]) { + SC_ABORT ("Only implemented for hypercubes.\n"); + } + else { + //Hypercubes + for (int idim = 0; idim < T8_ELEMENT_DIM[TEclass]; idim++) { + coords[idim] = elem->coords[idim] + ((vertex & (1 << idim)) >> idim) * element_get_len (elem->level); + } + } + } +}; + +#endif /* T8_STANDALONE_IMPLEMENTATION_HXX */ diff --git a/src/t8_types/t8_operators.hxx b/src/t8_types/t8_operators.hxx new file mode 100644 index 0000000000..f226e6804a --- /dev/null +++ b/src/t8_types/t8_operators.hxx @@ -0,0 +1,290 @@ +/* +This file is part of t8code. +t8code is a C library to manage a collection (a forest) of multiple +connected adaptive space-trees of general element classes in parallel. + +Copyright (C) 2024 the developers + +t8code 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. + +t8code 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 t8code; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef T8_OPERATORS_HXX +#define T8_OPERATORS_HXX + +#include +#include + +/** + * \file This file provides the CRTP pattern for operators. + * The operators can be used by a \a T8Type to extend the functionality of the type. + */ + +/** + * \brief The CRTP pattern for operators. + * + * \tparam TUnderlying + * \tparam crtpType + */ +template class crtpType> +struct t8_crtp_operator +{ + constexpr TUnderlying& + underlying () noexcept + { + return static_cast (*this); + } + + constexpr const TUnderlying& + underlying () const noexcept + { + return static_cast (*this); + } +}; + +/* + * The following is a list of competences that can be added to a type. + * Each competence provides access to an operator of the underlying type. That way instead of + * typing `my_int.get() + my_other_int.get()` you can type `my_int + my_other_int`. + */ + +/** + * \brief A template for addable types. Provides the + operator. + * + * \tparam TUnderlying + */ +template +struct Addable: t8_crtp_operator +{ + + constexpr TUnderlying + operator+ (const TUnderlying& other) const noexcept + { + return TUnderlying (this->underlying ().get () + other.get ()); + } +}; + +/** + * \brief A template for subtractable types. Provides the - operator. + * + * \tparam TUnderlying + */ +template +struct Subtractable: t8_crtp_operator +{ + constexpr TUnderlying + operator- (const TUnderlying& other) const noexcept + { + return TUnderlying (this->underlying ().get () - other.get ()); + } +}; + +/** + * \brief A template for multipliable types. Provides the * operator. + * + * \tparam TUnderlying + */ +template +struct Multipliable: t8_crtp_operator +{ + constexpr TUnderlying + operator* (const TUnderlying& other) const noexcept + { + return TUnderlying (this->underlying ().get () * other.get ()); + } +}; + +/** + * \brief A template for dividable types. Provides the / operator. + * + * \tparam TUnderlying + */ +template +struct Dividable: t8_crtp_operator +{ + constexpr TUnderlying + operator/ (const TUnderlying& other) const noexcept + { + return TUnderlying (this->underlying ().get () / other.get ()); + } +}; + +/** + * \brief A template for add-assignable types. Provides the += operator. + * + * \tparam TUnderlying + */ +template +struct AddAssignable: t8_crtp_operator +{ + constexpr TUnderlying& + operator+= (const TUnderlying& other) noexcept + { + this->underlying ().get () += other.get (); + return this->underlying (); + } +}; + +/** + * \brief A template for incrementable types. Provides the ++ operator. + * + * \tparam TUnderlying + * + * \note The operator is a prefix operator. + */ +template +struct PrefixIncrementable: t8_crtp_operator +{ + TUnderlying& + operator++ () noexcept + { + ++this->underlying ().get (); + return this->underlying (); + } +}; + +/** + * \brief A template for decrementable types. Provides the -- operator. + * + * \tparam TUnderlying + * + * \note The operator is a prefix operator. + */ +template +struct PrefixDecrementable: t8_crtp_operator +{ + TUnderlying& + operator-- () noexcept + { + --this->underlying ().get (); + return this->underlying (); + } +}; + +template +struct Printable: t8_crtp_operator +{ + friend std::ostream& + operator<< (std::ostream& os, const TUnderlying& obj) + { + os << obj.get (); + return os; + } +}; + +/** + * \brief A template for swapping types. Used to make a type swappable. + * + * \tparam TUnderlying + */ +template +struct Swapable: t8_crtp_operator +{ + constexpr void + swap (TUnderlying& lhs, TUnderlying& other) noexcept + { + std::swap (lhs.get (), other.get ()); + } +}; + +/** + * \brief A template for equality comparable types. Provides the == operator. + * + * \tparam TUnderlying + */ +template +struct EqualityComparable: t8_crtp_operator +{ + friend constexpr bool + operator== (const TUnderlying& lhs, const TUnderlying& rhs) noexcept + { + return lhs.get () == rhs.get (); + } + + constexpr bool + operator!= (TUnderlying const& other) const + { + return this->underlying ().get () != other.get (); + } +}; + +/** + * \brief A template for hashable types. Used to make a type hashable. + * + * \tparam TUnderlying + */ +template +struct Hashable +{ + static constexpr bool is_hashable = true; +}; + +/** + * \brief A template for random accessible types. Provides the [] operator. + * + * \tparam TUnderlying + */ +template +struct RandomAccessible: t8_crtp_operator +{ + auto + operator[] (std::size_t index) -> decltype (auto) + { + return this->underlying ().get ()[index]; + } + + auto + operator[] (std::size_t index) const -> decltype (auto) + { + return this->underlying ().get ()[index]; + } + + auto + begin () -> decltype (auto) + { + return this->underlying ().get ().begin (); + } + + auto + begin () const -> decltype (auto) + { + return this->underlying ().get ().begin (); + } + + auto + end () -> decltype (auto) + { + return this->underlying ().get ().end (); + } + + auto + end () const -> decltype (auto) + { + return this->underlying ().get ().end (); + } + + auto + data () -> decltype (auto) + { + return this->underlying ().get ().data (); + } + + auto + data () const -> decltype (auto) + { + return this->underlying ().get ().data (); + } +}; + +#endif // T8_OPERATORS_HXX diff --git a/src/t8_types/t8_type.hxx b/src/t8_types/t8_type.hxx new file mode 100644 index 0000000000..35a461e431 --- /dev/null +++ b/src/t8_types/t8_type.hxx @@ -0,0 +1,125 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2024 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** + * \file This files gives a template for strong types in t8code. + */ + +#ifndef T8_TYPE_HXX +#define T8_TYPE_HXX + +#include +#include + +/** + * \brief An implementation of strong type with additional competences. + * + * This class template allows the creation of a type that can be extended with + * multiple competences. Each competence is a template class that takes the + * main type as a template parameter. + * + * This is heavily inspired by (and taken from) https://www.fluentcpp.com/2016/12/08/strong-types-for-strong-interfaces/ + * + * \tparam T The type of the value to be stored. + * \tparam Parameter An additional parameter for the type. + * \tparam competence Variadic template parameter for the competences. + */ +template class... competence> +class T8Type: public competence>... { + public: + using value_type = T; + + explicit constexpr T8Type () = default; + + explicit constexpr T8Type (const T& value): value_ (value) + { + } + + /** + * \brief Construct a new T8Type object + * + * \tparam T_ref + * \param value + * + * \note This constructor is only enabled if T is not a reference. + */ + template + explicit constexpr T8Type (T&& value, + typename std::enable_if {}, std::nullptr_t>::type = nullptr) + : value_ (std::move (value)) + { + } + + constexpr T8Type& + operator= (const T& value) + { + value_ = value; + return *this; + } + + constexpr T& + get () noexcept + { + return value_; + } + + constexpr T const& + get () const noexcept + { + return std::move (value_); + } + + private: + T value_; +}; + +namespace std +{ +/** + * \brief Functor for hashing T8Type objects. + * + * This struct defines a functor that computes the hash value of a T8Type object. + * It uses the std::hash function to generate the hash value based on the underlying + * type T of the T8Type object. + * + * \tparam T The underlying type of the T8Type object. + * \tparam Parameter Additional parameters for the T8Type object. + * \tparam competence Variadic template parameters for additional competencies. + * + * \note This functor is enabled only if the T8Type object is hashable, as determined + * by the is_hashable member of the T8TypeImpl type. + */ +template class... competence> +struct hash> +{ + using T8TypeImpl = T8Type; + using checkIfHashable = typename std::enable_if::type; + + size_t + operator() (T8Type const& x) const + { + return std::hash {}(x.get ()); + } +}; +} // namespace std + +#endif /* T8_TYPE_HXX */ diff --git a/src/t8_types/t8_vec.cxx b/src/t8_types/t8_vec.cxx new file mode 100644 index 0000000000..9c6e0af435 --- /dev/null +++ b/src/t8_types/t8_vec.cxx @@ -0,0 +1,171 @@ +/* +This file is part of t8code. +t8code is a C library to manage a collection (a forest) of multiple +connected adaptive space-trees of general element classes in parallel. + +Copyright (C) 2024 the developers + +t8code 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. + +t8code 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 t8code; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include + +double +t8_norm (const double vec[3]) +{ + const t8_3D_vec *vec_array = reinterpret_cast (vec); + return t8_norm (*vec_array); +} + +void +t8_normalize (double vec[3]) +{ + t8_3D_vec *vec_array = reinterpret_cast (vec); + t8_normalize (*vec_array); +} + +void +t8_copy (const double vec_in[3], double vec_out[3]) +{ + const t8_3D_vec *vec_array_in = reinterpret_cast (vec_in); + t8_3D_vec *vec_array_out = reinterpret_cast (vec_out); + t8_copy (*vec_array_in, *vec_array_out); +} + +double +t8_dist (const double vec_x[3], const double vec_y[3]) +{ + const t8_3D_point *vec_array_x = reinterpret_cast (vec_x); + const t8_3D_point *vec_array_y = reinterpret_cast (vec_y); + return t8_dist (*vec_array_x, *vec_array_y); +} + +void +t8_ax (double vec_x[3], const double alpha) +{ + t8_3D_vec *vec_array_x = reinterpret_cast (vec_x); + t8_ax (*vec_array_x, alpha); +} + +void +t8_axy (const double vec_x[3], double vec_y[3], const double alpha) +{ + const t8_3D_vec *vec_array_x = reinterpret_cast (vec_x); + t8_3D_vec *vec_array_y = reinterpret_cast (vec_y); + t8_axy (*vec_array_x, *vec_array_y, alpha); +} + +void +t8_axb (const double vec_x[3], double vec_y[3], const double alpha, const double b) +{ + const t8_3D_vec *vec_array_x = reinterpret_cast (vec_x); + t8_3D_vec *vec_array_y = reinterpret_cast (vec_y); + t8_axb (*vec_array_x, *vec_array_y, alpha, b); +} + +void +t8_axpy (const double vec_x[3], double vec_y[3], const double alpha) +{ + const t8_3D_vec *vec_array_x = reinterpret_cast (vec_x); + t8_3D_vec *vec_array_y = reinterpret_cast (vec_y); + t8_axpy (*vec_array_x, *vec_array_y, alpha); +} + +void +t8_axpyz (const double vec_x[3], const double vec_y[3], double vec_z[3], const double alpha) +{ + const t8_3D_vec *vec_array_x = reinterpret_cast (vec_x); + const t8_3D_vec *vec_array_y = reinterpret_cast (vec_y); + t8_3D_vec *vec_array_z = reinterpret_cast (vec_z); + t8_axpyz (*vec_array_x, *vec_array_y, *vec_array_z, alpha); +} + +double +t8_dot (const double vec_x[3], const double vec_y[3]) +{ + const t8_3D_vec *vec_array_x = reinterpret_cast (vec_x); + const t8_3D_vec *vec_array_y = reinterpret_cast (vec_y); + return t8_dot (*vec_array_x, *vec_array_y); +} + +void +t8_cross_3D (const double vec_x[3], const double vec_y[3], double cross[3]) +{ + const t8_3D_vec *vec_array_x = reinterpret_cast (vec_x); + const t8_3D_vec *vec_array_y = reinterpret_cast (vec_y); + t8_3D_vec *cross_array = reinterpret_cast (cross); + t8_cross_3D (*vec_array_x, *vec_array_y, *cross_array); +} + +double +t8_cross_2D (const double vec_x[2], const double vec_y[2]) +{ + const t8_vec<2> *vec_array_x = reinterpret_cast *> (vec_x); + const t8_vec<2> *vec_array_y = reinterpret_cast *> (vec_y); + return t8_cross_2D (*vec_array_x, *vec_array_y); +} + +void +t8_diff (const double vec_x[3], const double vec_y[3], double diff[3]) +{ + const t8_3D_vec *vec_array_x = reinterpret_cast (vec_x); + const t8_3D_vec *vec_array_y = reinterpret_cast (vec_y); + t8_3D_vec *diff_array = reinterpret_cast (diff); + t8_diff (*vec_array_x, *vec_array_y, *diff_array); +} + +int +t8_eq (const double vec_x[3], const double vec_y[3], const double tol) +{ + const t8_3D_vec *vec_array_x = reinterpret_cast (vec_x); + const t8_3D_vec *vec_array_y = reinterpret_cast (vec_y); + return t8_eq (*vec_array_x, *vec_array_y, tol); +} + +void +t8_rescale (double vec[3], const double new_length) +{ + t8_3D_vec *vec_array = reinterpret_cast (vec); + t8_rescale (*vec_array, new_length); +} + +void +t8_normal_of_tri (const double p1[3], const double p2[3], const double p3[3], double normal[3]) +{ + const t8_3D_vec *p1_array = reinterpret_cast (p1); + const t8_3D_vec *p2_array = reinterpret_cast (p2); + const t8_3D_vec *p3_array = reinterpret_cast (p3); + t8_3D_vec *normal_array = reinterpret_cast (normal); + t8_normal_of_tri (*p1_array, *p2_array, *p3_array, *normal_array); +} + +void +t8_orthogonal_tripod (const double v1[3], double v2[3], double v3[3]) +{ + const t8_3D_vec *v1_array = reinterpret_cast (v1); + t8_3D_vec *v2_array = reinterpret_cast (v2); + t8_3D_vec *v3_array = reinterpret_cast (v3); + t8_orthogonal_tripod (*v1_array, *v2_array, *v3_array); +} + +void +t8_swap (double p1[3], double p2[3]) +{ + t8_3D_vec *p1_array = reinterpret_cast (p1); + t8_3D_vec *p2_array = reinterpret_cast (p2); + std::swap (*p1_array, *p2_array); +} diff --git a/src/t8_types/t8_vec.h b/src/t8_types/t8_vec.h new file mode 100644 index 0000000000..4d094812a3 --- /dev/null +++ b/src/t8_types/t8_vec.h @@ -0,0 +1,180 @@ +/* +This file is part of t8code. +t8code is a C library to manage a collection (a forest) of multiple +connected adaptive space-trees of general element classes in parallel. + +Copyright (C) 2025 the developers + +t8code 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. + +t8code 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 t8code; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_vec.h + * Provide vector operations for 3D vectors. + */ + +#ifndef T8_VEC_H +#define T8_VEC_H + +#include + +T8_EXTERN_C_BEGIN (); + +/** Vector norm. + * \param [in] vec A 3D vector. + * \return The norm of \a vec. + */ +double +t8_norm (const double vec[3]); + +/** Normalize a vector. + * \param [in,out] vec A 3D vector. + */ +void +t8_normalize (double vec[3]); + +/** Make a copy of a vector. + * \param [in] vec_in + * \param [out] vec_out + */ +void +t8_copy (const double vec_in[3], double vec_out[3]); + +/** Euclidean distance of X and Y. + * \param [in] vec_x A 3D vector. + * \param [in] vec_y A 3D vector. + * \return The euclidean distance. + * Equivalent to norm (X-Y). + */ +double +t8_dist (const double vec_x[3], const double vec_y[3]); + +/** Compute X = alpha * X + * \param [in,out] vec_x A 3D vector. On output set to \a alpha * \a vec_x. + * \param [in] alpha A factor. + */ +void +t8_ax (double vec_x[3], const double alpha); + +/** Compute Y = alpha * X + * \param [in] vec_x A 3D vector. + * \param [out] vec_z On output set to \a alpha * \a vec_x. + * \param [in] alpha A factor. + */ +void +t8_axy (const double vec_x[3], double vec_y[3], const double alpha); + +/** Y = alpha * X + b + * \param [in] vec_x A 3D vector. + * \param [out] vec_y On input, a 3D vector. + * On output set to \a alpha * \a vec_x + \a b. + * \param [in] alpha A factor. + * \param [in] b An offset. + * \note It is possible that vec_x = vec_y on input to overwrite x + */ +void +t8_axb (const double vec_x[3], double vec_y[3], const double alpha, const double b); +/** Y = Y + alpha * X + * \param [in] vec_x A 3D vector. + * \param [in,out] vec_y On input, a 3D vector. + * On output set \a to vec_y + \a alpha * \a vec_x + * \param [in] alpha A factor. + */ +void +t8_axpy (const double vec_x[3], double vec_y[3], const double alpha); + +/** Z = Y + alpha * X + * \param [in] vec_x A 3D vector. + * \param [in] vec_y A 3D vector. + * \param [out] vec_z On output set \a to vec_y + \a alpha * \a vec_x + */ +void +t8_axpyz (const double vec_x[3], const double vec_y[3], double vec_z[3], const double alpha); + +/** Dot product of X and Y. + * \param [in] vec_x A 3D vector. + * \param [in] vec_y A 3D vector. + * \return The dot product \a vec_x * \a vec_y + */ +double +t8_dot (const double vec_x[3], const double vec_y[3]); + +/** Cross product of X and Y + * \param [in] vec_x A 3D vector. + * \param [in] vec_y A 3D vector. + * \param [out] cross On output, the cross product of \a vec_x and \a vec_y. + */ +void +t8_cross_3D (const double vec_x[3], const double vec_y[3], double cross[3]); + +/** Cross product of X and Y + * \param [in] vec_x A 2D vector. + * \param [in] vec_y A 2D vector. + * \param [out] cross On output, the cross product of \a vec_x and \a vec_y. + */ +double +t8_cross_2D (const double vec_x[2], const double vec_y[2]); + +/** Compute the difference of two vectors. + * \param [in] vec_x A 3D vector. + * \param [in] vec_y A 3D vector. + * \param [out] diff On output, the difference of \a vec_x and \a vec_y. + */ +void +t8_diff (const double vec_x[3], const double vec_y[3], double diff[3]); + +/** + * Check the equality of two vectors elementwise + * + * \param[in] vec_x + * \param[in] vec_y + * \param[in] tol + * \return true, if the vectors are equal up to \a tol + */ +int +t8_eq (const double vec_x[3], const double vec_y[3], const double tol); + +/** Rescale a vector to a new length. + * \param [in,out] vec A 3D vector. + * \param [in] new_length New length of the vector. + */ +void +t8_rescale (double vec[3], const double new_length); + +/** Compute the normal of a triangle given by its three vertices. + * \param [in] p1 A 3D vector. + * \param [in] p2 A 3D vector. + * \param [in] p3 A 3D vector. + * \param [out] Normal vector of the triangle. (Not necessarily of length 1!) + */ +void +t8_normal_of_tri (const double p1[3], const double p2[3], const double p3[3], double normal[3]); + +/** Compute an orthogonal coordinate system from a given vector. + * \param [in] v1 3D vector. + * \param [out] v2 3D vector. + * \param [out] v3 3D vector. + */ +void +t8_orthogonal_tripod (const double v1[3], double v2[3], double v3[3]); +/** Swap the components of two vectors. + * \param [in,out] p1 A 3D vector. + * \param [in,out] p2 A 3D vector. + */ +void +t8_swap (double p1[3], double p2[3]); + +T8_EXTERN_C_END (); + +#endif /* T8_VEC_H */ diff --git a/src/t8_types/t8_vec.hxx b/src/t8_types/t8_vec.hxx new file mode 100644 index 0000000000..2490a27026 --- /dev/null +++ b/src/t8_types/t8_vec.hxx @@ -0,0 +1,293 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_vec.hxx + * We define routines to handle 3-dimensional vectors. + */ + +#ifndef T8_VEC_HXX +#define T8_VEC_HXX + +#include +#include +#include +// template +// using t8_point_t = std::array; +// typedef t8_point_t<3> t8_3D_point; + +#include +#include + +template +struct t8_vec_tag +{ +}; + +template +struct t8_point_tag +{ +}; + +template +using t8_vec = T8Type, t8_vec_tag, EqualityComparable, Swapable, RandomAccessible>; +using t8_3D_vec = t8_vec<3>; + +template +using t8_point = T8Type, t8_point_tag, EqualityComparable, Swapable, RandomAccessible>; +using t8_3D_point = t8_point<3>; + +/** Vector norm. + * \param [in] vec An N-dimensional vector. + * \return The norm of \a vec. + */ +template +static inline double +t8_norm (const t8_vec &vec) +{ + return std::sqrt (std::inner_product (vec.begin (), vec.end (), vec.begin (), 0.0)); +} + +/** Normalize a vector. + * \param [in,out] vec An N-dimensional vector. + */ +template +constexpr void +t8_normalize (t8_vec &vec) +{ + const double norm = t8_norm (vec); + std::transform (vec.begin (), vec.end (), vec.begin (), [norm] (double v) { return v / norm; }); +} + +/** Make a copy of a vector or point. + * \param [in] src +t8_type_copy (T const &src, T const &dest) +static inline void +*/ +template +static inline void +t8_copy (const T &src, T &dest); + +template +constexpr void +t8_copy (const t8_vec &src, t8_vec &dest) +{ + std::copy (src.begin (), src.end (), dest.begin ()); +} + +template +constexpr void +t8_copy (const t8_point &src, t8_point &dest) +{ + std::copy (src.begin (), src.end (), dest.begin ()); +} + +/** Euclidean distance of X and Y. + * \param [in] point_x An N-dimensional point. + * \param [in] point_y An N-dimensional point. + * \return The euclidean distance. + * Equivalent to norm (X-Y). + */ +template +static inline double +t8_dist (const t8_point &point_x, const t8_point &point_y) +{ + double dist = std::inner_product (point_x.begin (), point_x.end (), point_y.begin (), 0.0, std::plus (), + [] (double x, double y) { return (x - y) * (x - y); }); + return std::sqrt (dist); +} + +/** Compute X = alpha * X + * \param [in,out] vec_x An N-dimensional vector. On output set to \a alpha * \a vec_x. + * \param [in] alpha A factor. + */ +template +constexpr void +t8_ax (t8_vec &vec_x, const double alpha) +{ + std::transform (vec_x.begin (), vec_x.end (), vec_x.begin (), [alpha] (double v) { return v * alpha; }); +} + +/** Compute Y = alpha * X + * \param [in] vec_x An N-dimensional vector. + * \param [out] vec_z On output set to \a alpha * \a vec_x. + * \param [in] alpha A factor. + */ +template +constexpr void +t8_axy (const t8_vec &vec_x, t8_vec &vec_y, const double alpha) +{ + std::transform (vec_x.begin (), vec_x.end (), vec_y.begin (), [alpha] (double v) { return v * alpha; }); +} + +/** Y = alpha * X + b + * \param [in] vec_x An N-dimensional vector. + * \param [out] vec_y On input, An N-dimensional vector. + * On output set to \a alpha * \a vec_x + \a b. + * \param [in] alpha A factor. + * \param [in] b An offset. + * \note It is possible that vec_x = vec_y on input to overwrite x + */ +template +constexpr void +t8_axb (const t8_vec &vec_x, t8_vec &vec_y, const double alpha, const double b) +{ + std::transform (vec_x.begin (), vec_x.end (), vec_y.begin (), [alpha, b] (double v) { return alpha * v + b; }); +} + +/** Y = Y + alpha * X + * \param [in] vec_x An N-dimensional vector. + * \param [in,out] vec_y On input, An N-dimensional vector. + * On output set \a to vec_y + \a alpha * \a vec_x + * \param [in] alpha A factor. + */ +template +constexpr void +t8_axpy (const t8_vec &vec_x, t8_vec &vec_y, const double alpha) +{ + std::transform (vec_x.begin (), vec_x.end (), vec_y.begin (), vec_y.begin (), + [alpha] (double x, double y) { return y + alpha * x; }); +} + +/** Z = Y + alpha * X + * \param [in] vec_x An N-dimensional vector. + * \param [in] vec_y An N-dimensional vector. + * \param [out] vec_z On output set \a to vec_y + \a alpha * \a vec_x + */ +template +constexpr void +t8_axpyz (const t8_vec &vec_x, const t8_vec &vec_y, t8_vec &vec_z, const double alpha) +{ + std::transform (vec_x.begin (), vec_x.end (), vec_y.begin (), vec_z.begin (), + [alpha] (double x, double y) { return y + alpha * x; }); +} + +/** Dot product of X and Y. + * \param [in] vec_x An N-dimensional vector. + * \param [in] vec_y An N-dimensional vector. + * \return The dot product \a vec_x * \a vec_y + */ +template +constexpr double +t8_dot (const t8_vec &vec_x, const t8_vec &vec_y) +{ + return std::inner_product (vec_x.begin (), vec_x.end (), vec_y.begin (), 0.0); +} +/** Cross product of X and Y + * \param [in] vec_x A 2D vector. + * \param [in] vec_y A 2D vector. + * \param [out] cross On output, the cross product of \a vec_x and \a vec_y. + */ +static inline double +t8_cross_2D (const t8_vec<2> &vec_x, const t8_vec<2> &vec_y) +{ + return vec_x[0] * vec_y[1] - vec_x[1] * vec_y[0]; +} + +/** Cross product of X and Y + * \param [in] vec_x A 3D vector. + * \param [in] vec_y A 3D vector. + * \param [out] cross On output, the cross product of \a vec_x and \a vec_y. + */ +static inline void +t8_cross_3D (const t8_3D_vec &vec_x, const t8_3D_vec &vec_y, t8_3D_vec &cross) +{ + cross[0] = vec_x[1] * vec_y[2] - vec_x[2] * vec_y[1]; + cross[1] = vec_x[2] * vec_y[0] - vec_x[0] * vec_y[2]; + cross[2] = vec_x[0] * vec_y[1] - vec_x[1] * vec_y[0]; +} + +/** Compute the difference of two vectors. + * \param [in] vec_x An N-dimensional vector. + * \param [in] vec_y An N-dimensional vector. + * \param [out] diff On output, the difference of \a vec_x and \a vec_y. + */ +template +constexpr void +t8_diff (const t8_vec &vec_x, const t8_vec &vec_y, t8_vec &diff) +{ + std::transform (vec_x.begin (), vec_x.end (), vec_y.begin (), diff.begin (), std::minus ()); +} + +/** + * Check the equality of two vectors or points elementwise + * + * \param[in] vec_x + * \param[in] vec_y + * \param[in] tol + * \return true, if the vectors are equal up to \a tol + */ +template +constexpr bool +t8_eq (const T &vec_x, const T &vec_y, const double tol) +{ + return std::equal (vec_x.begin (), vec_x.end (), vec_y.begin (), + [tol] (double x, double y) { return std::fabs (x - y) <= tol; }); +} + +/** Rescale a vector to a new length. + * \param [in,out] vec An N-dimensional vector. + * \param [in] new_length New length of the vector. + */ +template +static inline void +t8_rescale (t8_vec &vec, const double new_length) +{ + t8_normalize (vec); + t8_ax (vec, new_length); +} + +/** Compute the normal of a triangle given by its three vertices. + * \param [in] p1 A 3D vector. + * \param [in] p2 A 3D vector. + * \param [in] p3 A 3D vector. + * \param [out] Normal vector of the triangle. (Not necessarily of length 1!) + */ +static inline void +t8_normal_of_tri (const t8_3D_vec &p1, const t8_3D_vec &p2, const t8_3D_vec &p3, t8_3D_vec &normal) +{ + t8_3D_vec a; + t8_3D_vec b; + std::transform (p2.begin (), p2.end (), p1.begin (), a.begin (), std::minus ()); + std::transform (p3.begin (), p3.end (), p1.begin (), b.begin (), std::minus ()); + t8_cross_3D (a, b, normal); +} + +/** Compute an orthogonal coordinate system from a given vector. + * \param [in] v1 3D vector. + * \param [out] v2 3D vector. + * \param [out] v3 3D vector. + */ +static inline void +t8_orthogonal_tripod (const t8_3D_vec &v1, t8_3D_vec &v2, t8_3D_vec &v3) +{ + v2[0] = v1[1]; + v2[1] = v1[2]; + v2[2] = -v1[0]; + + t8_axpy<3> (v1, v2, -t8_dot<3> (v1, v2)); + t8_cross_3D (v1, v2, v3); + + t8_normalize<3> (v2); + t8_normalize<3> (v3); +} + +#endif /* !T8_VEC_HXX */ diff --git a/src/t8_vec.h b/src/t8_vec.h deleted file mode 100644 index 6db681486f..0000000000 --- a/src/t8_vec.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - This file is part of t8code. - t8code is a C library to manage a collection (a forest) of multiple - connected adaptive space-trees of general element classes in parallel. - - Copyright (C) 2015 the developers - - t8code 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. - - t8code 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 t8code; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/** \file t8_vec.h - * We define routines to handle 3-dimensional vectors. - */ - -#ifndef T8_VEC_H -#define T8_VEC_H - -#include - -/** Vector norm. - * \param [in] vec A 3D vector. - * \return The norm of \a vec. - */ -static inline double -t8_vec_norm (const double vec[3]) -{ - double norm = 0; - - for (int i = 0; i < 3; i++) { - norm += vec[i] * vec[i]; - } - return sqrt (norm); -} - -/** Normalize a vector. - * \param [in,out] vec A 3D vector. - */ -static inline void -t8_vec_normalize (double vec[3]) -{ - const double inv_norm = 1.0 / t8_vec_norm (vec); - - for (int i = 0; i < 3; i++) { - vec[i] *= inv_norm; - } -} - -/** Make a copy of a vector. - * \param [in] vec_in - * \param [out] vec_out - */ -static inline void -t8_vec_copy (const double vec_in[3], double vec_out[3]) -{ - for (int i = 0; i < 3; i++) { - vec_out[i] = vec_in[i]; - } -} - -/** Euclidean distance of X and Y. - * \param [in] vec_x A 3D vector. - * \param [in] vec_y A 3D vector. - * \return The euclidean distance. - * Equivalent to norm (X-Y). - */ -static inline double -t8_vec_dist (const double vec_x[3], const double vec_y[3]) -{ - double dist = 0; - - for (int i = 0; i < 3; i++) { - dist += SC_SQR (vec_x[i] - vec_y[i]); - } - return sqrt (dist); -} - -/** Compute X = alpha * X - * \param [in,out] vec_x A 3D vector. On output set to \a alpha * \a vec_x. - * \param [in] alpha A factor. - */ -static inline void -t8_vec_ax (double vec_x[3], const double alpha) -{ - for (int i = 0; i < 3; i++) { - vec_x[i] *= alpha; - } -} - -/** Compute Y = alpha * X - * \param [in] vec_x A 3D vector. - * \param [out] vec_z On output set to \a alpha * \a vec_x. - * \param [in] alpha A factor. - */ -static inline void -t8_vec_axy (const double vec_x[3], double vec_y[3], const double alpha) -{ - for (int i = 0; i < 3; i++) { - vec_y[i] = vec_x[i] * alpha; - } -} - -/** Y = alpha * X + b - * \param [in] vec_x A 3D vector. - * \param [out] vec_y On input, a 3D vector. - * On output set to \a alpha * \a vec_x + \a b. - * \param [in] alpha A factor. - * \param [in] b An offset. - * \note It is possible that vec_x = vec_y on input to overwrite x - */ -static inline void -t8_vec_axb (const double vec_x[3], double vec_y[3], const double alpha, const double b) -{ - for (int i = 0; i < 3; i++) { - vec_y[i] = alpha * vec_x[i] + b; - } -} - -/** Y = Y + alpha * X - * \param [in] vec_x A 3D vector. - * \param [in,out] vec_y On input, a 3D vector. - * On output set \a to vec_y + \a alpha * \a vec_x - * \param [in] alpha A factor. - */ -static inline void -t8_vec_axpy (const double vec_x[3], double vec_y[3], const double alpha) -{ - for (int i = 0; i < 3; i++) { - vec_y[i] += alpha * vec_x[i]; - } -} - -/** Z = Y + alpha * X - * \param [in] vec_x A 3D vector. - * \param [in] vec_y A 3D vector. - * \param [out] vec_z On output set \a to vec_y + \a alpha * \a vec_x - */ -static inline void -t8_vec_axpyz (const double vec_x[3], const double vec_y[3], double vec_z[3], const double alpha) -{ - for (int i = 0; i < 3; i++) { - vec_z[i] = vec_y[i] + alpha * vec_x[i]; - } -} - -/** Dot product of X and Y. - * \param [in] vec_x A 3D vector. - * \param [in] vec_y A 3D vector. - * \return The dot product \a vec_x * \a vec_y - */ -static inline double -t8_vec_dot (const double vec_x[3], const double vec_y[3]) -{ - double dot = 0; - - for (int i = 0; i < 3; i++) { - dot += vec_x[i] * vec_y[i]; - } - return dot; -} - -/** Cross product of X and Y - * \param [in] vec_x A 3D vector. - * \param [in] vec_y A 3D vector. - * \param [out] cross On output, the cross product of \a vec_x and \a vec_y. - */ -static inline void -t8_vec_cross (const double vec_x[3], const double vec_y[3], double cross[3]) -{ - for (int i = 0; i < 3; i++) { - cross[i] = vec_x[(i + 1) % 3] * vec_y[(i + 2) % 3] - vec_x[(i + 2) % 3] * vec_y[(i + 1) % 3]; - } -} - -/** Compute the difference of two vectors. - * \param [in] vec_x A 3D vector. - * \param [in] vec_y A 3D vector. - * \param [out] diff On output, the difference of \a vec_x and \a vec_y. - */ -static inline void -t8_vec_diff (const double vec_x[3], const double vec_y[3], double diff[3]) -{ - for (int i = 0; i < 3; i++) { - diff[i] = vec_x[i] - vec_y[i]; - } -} - -/** - * Check the equality of two vectors elementwise - * - * \param[in] vec_x - * \param[in] vec_y - * \param[in] tol - * \return true, if the vectors are equal up to \a tol - */ -static inline int -t8_vec_eq (const double vec_x[3], const double vec_y[3], const double tol) -{ - T8_ASSERT (tol > 0); - return fabs (vec_x[0] - vec_y[0]) <= tol && fabs (vec_x[1] - vec_y[1]) <= tol && fabs (vec_x[2] - vec_y[2]) <= tol; -} - -/** Rescale a vector to a new length. - * \param [in,out] vec A 3D vector. - * \param [in] new_length New length of the vector. - */ -static inline void -t8_vec_rescale (double vec[3], const double new_length) -{ - t8_vec_normalize (vec); - t8_vec_ax (vec, new_length); -} - -/** Compute the normal of a triangle given by its three vertices. - * \param [in] p1 A 3D vector. - * \param [in] p2 A 3D vector. - * \param [in] p3 A 3D vector. - * \param [out] Normal vector of the triangle. (Not necessarily of length 1!) - */ -static inline void -t8_vec_tri_normal (const double p1[3], const double p2[3], const double p3[3], double normal[3]) -{ - double a[3]; /* First triangle side. */ - double b[3]; /* Second triangle side. */ - - a[0] = p2[0] - p1[0]; - a[1] = p2[1] - p1[1]; - a[2] = p2[2] - p1[2]; - - b[0] = p3[0] - p1[0]; - b[1] = p3[1] - p1[1]; - b[2] = p3[2] - p1[2]; - - t8_vec_cross (a, b, normal); -} - -/** Compute an orthogonal coordinate system from a given vector. - * \param [in] v1 3D vector. - * \param [out] v2 3D vector. - * \param [out] v3 3D vector. - */ -static inline void -t8_vec_orthogonal_tripod (const double v1[3], double v2[3], double v3[3]) -{ - v2[0] = v1[1]; - v2[1] = v1[2]; - v2[2] = -v1[0]; - - t8_vec_axpy (v1, v2, -t8_vec_dot (v1, v2)); - t8_vec_cross (v1, v2, v3); - - t8_vec_normalize (v2); - t8_vec_normalize (v3); -} - -/** Swap the components of two vectors. - * \param [in,out] p1 A 3D vector. - * \param [in,out] p2 A 3D vector. - */ -static inline void -t8_vec_swap (double p1[3], double p2[3]) -{ - double tmp; - for (int i = 0; i < 3; i++) { - tmp = p1[i]; - p1[i] = p2[i]; - p2[i] = tmp; - } -} - -#endif /* !T8_VEC_H */ diff --git a/src/t8_vtk/t8_vtk_write_ASCII.cxx b/src/t8_vtk/t8_vtk_write_ASCII.cxx index 1f345ed60e..027d336b30 100644 --- a/src/t8_vtk/t8_vtk_write_ASCII.cxx +++ b/src/t8_vtk/t8_vtk_write_ASCII.cxx @@ -24,7 +24,7 @@ #include "t8_vtk/t8_vtk_writer_helper.hxx" #include #include -#include +#include #include "t8_forest/t8_forest_types.h" #include "t8_cmesh/t8_cmesh_trees.h" #include "t8_cmesh/t8_cmesh_types.h" diff --git a/src/t8_vtk/t8_vtk_writer.hxx b/src/t8_vtk/t8_vtk_writer.hxx index d7b1325307..6c028c9a5e 100644 --- a/src/t8_vtk/t8_vtk_writer.hxx +++ b/src/t8_vtk/t8_vtk_writer.hxx @@ -34,7 +34,7 @@ #include #include -#include +#include #if T8_WITH_VTK #include @@ -138,6 +138,83 @@ class vtk_writer { bool write_ASCII (const grid_t grid); + /** + * Set the write treeid flag. Set to true, if you want to write the tree id of every element. + * + * \param[in] write_treeid true or false + */ + inline void + set_write_treeid (const bool write_treeid) + { + this->write_treeid = write_treeid; + } + + /** + * Set the write mpirank flag. Set to true, if you want to write the mpirank of every element. + * + * \param[in] write_mpirank true or false + */ + inline void + set_write_mpirank (const bool write_mpirank) + { + this->write_mpirank = write_mpirank; + } + + /** + * Set the write level flag. Set to true, if you want to write the level of every element. + * + * \param[in] write_level true or false + */ + inline void + set_write_level (const bool write_level) + { + this->write_level = write_level; + } + + /** + * Set the write element id flag. Set to true, if you want to write the element id of every element. + * + * \param[in] write_element_id true or false + */ + inline void + set_write_element_id (const bool write_element_id) + { + this->write_element_id = write_element_id; + } + + /** + * Set the write ghosts flag. Set to true, if you want to write the ghost elements, too. + * + * \param[in] write_ghosts true or false + */ + inline void + set_write_ghosts (const bool write_ghosts) + { + this->write_ghosts = write_ghosts; + } + + /** + * Set the curved flag. Set to true, if you want to use quadratic vtk cells. + * Uses the geometry of the grid to evaluate points between corners. + * + * \param[in] curved_flag true or false + */ + inline void + set_curved_flag (const bool curved_flag) + { + this->curved_flag = curved_flag; + } + + /** + * Set the fileprefix for the output files. + * \param[in] fileprefix + */ + inline void + set_fileprefix (std::string fileprefix) + { + this->fileprefix = fileprefix; + } + private: #if T8_WITH_VTK /** diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 251ef14a0f..31a6e027a2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,5 @@ add_library( gtest ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/googletest-mpi/gtest/gtest-all.cc ) -target_include_directories( gtest PUBLIC ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/googletest-mpi ${CMAKE_CURRENT_LIST_DIR}/.. ) +target_include_directories( gtest SYSTEM PUBLIC ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/googletest-mpi ${CMAKE_CURRENT_LIST_DIR}/.. ) function( add_t8_test ) set( options "" ) @@ -74,6 +74,9 @@ add_t8_test( NAME t8_gtest_basics_serial SOURCES t8_gtest_main.cxx t8 add_t8_test( NAME t8_gtest_netcdf_linkage_serial SOURCES t8_gtest_main.cxx t8_gtest_netcdf_linkage.cxx ) add_t8_test( NAME t8_gtest_vtk_linkage_serial SOURCES t8_gtest_main.cxx t8_gtest_vtk_linkage.cxx ) +add_t8_test( NAME t8_gtest_type_serial SOURCES t8_gtest_main.cxx t8_types/t8_gtest_type.cxx ) +add_t8_test( NAME t8_gtest_random_accessible_serial SOURCES t8_gtest_main.cxx t8_types/t8_gtest_random_accessible.cxx ) + add_t8_test( NAME t8_gtest_hypercube_parallel SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_hypercube.cxx ) add_t8_test( NAME t8_gtest_cmesh_readmshfile_serial SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_readmshfile.cxx ) add_t8_test( NAME t8_gtest_cmesh_copy_serial SOURCES t8_gtest_main.cxx t8_cmesh/t8_gtest_cmesh_copy.cxx ) @@ -126,7 +129,7 @@ add_t8_test( NAME t8_gtest_vtk_writer_parallel SOURCES t8_gtest_main.cxx t8_IO/ add_t8_test( NAME t8_gtest_nca_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_nca.cxx ) add_t8_test( NAME t8_gtest_pyra_connectivity_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_pyra_connectivity.cxx ) add_t8_test( NAME t8_gtest_face_neigh_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_face_neigh.cxx ) -add_t8_test( NAME t8_gtest_init_linear_id_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_init_linear_id.cxx ) +add_t8_test( NAME t8_gtest_get_linear_id_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_get_linear_id.cxx ) add_t8_test( NAME t8_gtest_ancestor_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_ancestor.cxx ) add_t8_test( NAME t8_gtest_ancestor_id_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_ancestor_id.cxx ) add_t8_test( NAME t8_gtest_element_count_leaves_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_element_count_leaves.cxx ) @@ -142,6 +145,9 @@ add_t8_test( NAME t8_gtest_child_parent_face_serial SOURCES t8_gtest_main.cx add_t8_test( NAME t8_gtest_pack_unpack_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_pack_unpack.cxx ) add_t8_test( NAME t8_gtest_root_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_root.cxx ) add_t8_test( NAME t8_gtest_scheme_consistency_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_scheme_consistency.cxx ) +add_t8_test( NAME t8_gtest_input_equal_output_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_input_equal_output.cxx ) +add_t8_test( NAME t8_gtest_face_corner_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_face_corner.cxx ) +add_t8_test( NAME t8_gtest_set_linear_id_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_set_linear_id.cxx ) if( T8CODE_BUILD_FORTRAN_INTERFACE AND T8CODE_ENABLE_MPI ) add_t8_test( NAME t8_test_fortran_mpi_interface_init_parallel SOURCES api/t8_fortran_interface/t8_test_mpi_init.f90 ) diff --git a/test/t8_cmesh/t8_gtest_cmesh_partition.cxx b/test/t8_cmesh/t8_gtest_cmesh_partition.cxx index 9376cb60f0..0ed910f015 100644 --- a/test/t8_cmesh/t8_gtest_cmesh_partition.cxx +++ b/test/t8_cmesh/t8_gtest_cmesh_partition.cxx @@ -27,6 +27,7 @@ #include "t8_cmesh/t8_cmesh_partition.h" #include #include +#include /* We create a cmesh, partition it and repartition it several times. * At the end we result in the same partition as at the beginning and we @@ -34,20 +35,28 @@ * passed. */ -class t8_cmesh_partition_class: public testing::TestWithParam { +class t8_cmesh_partition_class: public testing::TestWithParam> { protected: void SetUp () override { - size_t found = GetParam ()->name.find (std::string ("empty")); + const int scheme_id = std::get<0> (GetParam ()); + scheme = create_from_scheme_id (scheme_id); + size_t found = std::get<1> (GetParam ())->name.find (std::string ("empty")); if (found != std::string::npos) { /* Tests not working for empty cmeshes */ GTEST_SKIP (); } - - cmesh_original = GetParam ()->cmesh_create (); + cmesh_original = std::get<1> (GetParam ())->cmesh_create (); } + void + TearDown () override + { + scheme->unref (); + } + t8_cmesh_t cmesh_original; + const t8_scheme *scheme; }; static void @@ -78,7 +87,8 @@ TEST_P (t8_cmesh_partition_class, test_cmesh_partition_concentrate) t8_cmesh_init (&cmesh_partition); t8_cmesh_set_derive (cmesh_partition, cmesh_original); /* Uniform partition according to level */ - t8_cmesh_set_partition_uniform (cmesh_partition, level, t8_scheme_new_default ()); + scheme->ref (); + t8_cmesh_set_partition_uniform (cmesh_partition, level, scheme); t8_cmesh_commit (cmesh_partition, sc_MPI_COMM_WORLD); test_cmesh_committed (cmesh_partition); @@ -118,7 +128,8 @@ TEST_P (t8_cmesh_partition_class, test_cmesh_partition_concentrate) for (int i = 0; i < 2; i++) { t8_cmesh_init (&cmesh_partition_new2); t8_cmesh_set_derive (cmesh_partition_new2, cmesh_partition_new1); - t8_cmesh_set_partition_uniform (cmesh_partition_new2, level, t8_scheme_new_default ()); + scheme->ref (); + t8_cmesh_set_partition_uniform (cmesh_partition_new2, level, scheme); t8_cmesh_commit (cmesh_partition_new2, sc_MPI_COMM_WORLD); cmesh_partition_new1 = cmesh_partition_new2; } @@ -128,5 +139,5 @@ TEST_P (t8_cmesh_partition_class, test_cmesh_partition_concentrate) } /* Test all cmeshes over all different inputs we get through their id */ -INSTANTIATE_TEST_SUITE_P (t8_gtest_cmesh_partition, t8_cmesh_partition_class, AllCmeshsParam, - pretty_print_base_example); +INSTANTIATE_TEST_SUITE_P (t8_gtest_cmesh_partition, t8_cmesh_partition_class, + testing::Combine (AllSchemeCollections, AllCmeshsParam), pretty_print_base_example_scheme); diff --git a/test/t8_cmesh/t8_gtest_multiple_attributes.cxx b/test/t8_cmesh/t8_gtest_multiple_attributes.cxx index 28ae877b12..74857a27a3 100644 --- a/test/t8_cmesh/t8_gtest_multiple_attributes.cxx +++ b/test/t8_cmesh/t8_gtest_multiple_attributes.cxx @@ -25,33 +25,35 @@ #include #include #include +#include /* Test if multiple attributes are partitioned correctly. */ /** Return a partitioned cmesh from \a cmesh. */ static t8_cmesh_t -t8_cmesh_partition_cmesh (t8_cmesh_t cmesh, sc_MPI_Comm comm) +t8_cmesh_partition_cmesh (t8_cmesh_t cmesh, const t8_scheme *scheme, sc_MPI_Comm comm) { t8_cmesh_t cmesh_partition; t8_cmesh_init (&cmesh_partition); t8_cmesh_set_derive (cmesh_partition, cmesh); - t8_cmesh_set_partition_uniform (cmesh_partition, 0, t8_scheme_new_default ()); + t8_cmesh_set_partition_uniform (cmesh_partition, 0, scheme); t8_cmesh_commit (cmesh_partition, comm); return cmesh_partition; } -class cmesh_multiple_attributes: public testing::TestWithParam { +class cmesh_multiple_attributes: public testing::TestWithParam> { protected: void SetUp () override { - num_trees = GetParam (); + const int scheme_id = std::get<0> (GetParam ()); + num_trees = std::get<1> (GetParam ()); cmesh_one_at = t8_cmesh_new_row_of_cubes (num_trees, 0, 0, sc_MPI_COMM_WORLD); - cmesh_one_at = t8_cmesh_partition_cmesh (cmesh_one_at, sc_MPI_COMM_WORLD); + cmesh_one_at = t8_cmesh_partition_cmesh (cmesh_one_at, create_from_scheme_id (scheme_id), sc_MPI_COMM_WORLD); cmesh_mult_at = t8_cmesh_new_row_of_cubes (num_trees, 1, 0, sc_MPI_COMM_WORLD); - cmesh_mult_at = t8_cmesh_partition_cmesh (cmesh_mult_at, sc_MPI_COMM_WORLD); + cmesh_mult_at = t8_cmesh_partition_cmesh (cmesh_mult_at, create_from_scheme_id (scheme_id), sc_MPI_COMM_WORLD); cmesh_mult_at_from_stash = t8_cmesh_new_row_of_cubes (num_trees, 1, 1, sc_MPI_COMM_WORLD); } @@ -152,4 +154,5 @@ TEST_P (cmesh_multiple_attributes, multiple_attributes) } /* Test for different number of trees. */ -INSTANTIATE_TEST_SUITE_P (t8_gtest_multiple_attributes, cmesh_multiple_attributes, testing::Range (1, 10)); +INSTANTIATE_TEST_SUITE_P (t8_gtest_multiple_attributes, cmesh_multiple_attributes, + testing::Combine (AllSchemeCollections, testing::Range (1, 10))); diff --git a/test/t8_cmesh_generator/t8_cmesh_example_sets.hxx b/test/t8_cmesh_generator/t8_cmesh_example_sets.hxx index 6824cd7f15..6528cc2c10 100644 --- a/test/t8_cmesh_generator/t8_cmesh_example_sets.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_example_sets.hxx @@ -49,6 +49,14 @@ auto pretty_print_base_example = [] (const testing::TestParamInfo> &info) { + std::string name; + std::get<1> (info.param)->param_to_string (name); + name += std::string ("_scheme_") + std::to_string (std::get<0> (info.param)); + name += std::string ("_") + std::to_string (info.index); + return name; +}; + namespace cmesh_list { std::vector cart_prod_vec = { new_from_class::cmesh_example, diff --git a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_bigmesh_param.hxx b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_bigmesh_param.hxx index 003433d2a3..6e5e1b59d5 100644 --- a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_bigmesh_param.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_bigmesh_param.hxx @@ -51,7 +51,7 @@ example_set *cmesh_example std::make_pair (cmesh_params::eclasses.begin (), cmesh_params::eclasses.end ()), std::make_pair (cmesh_params::large_mesh.begin (), cmesh_params::large_mesh.end ()), std::make_pair (cmesh_params::my_comms.begin (), cmesh_params::my_comms.end ()), bigmesh, make_param_string_wrapper, - "t8_cmesh_new_bigmesh_"); + "t8_cmesh_new_bigmesh"); } // namespace new_bigmesh #endif /* T8_CMESH_NEW_BIGMESH_PARAM_HXX */ diff --git a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_comm.hxx b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_comm.hxx index 34863a7c52..3714806adb 100644 --- a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_comm.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_comm.hxx @@ -52,18 +52,18 @@ std::vector> cmesh_functions = { t8_cmes t8_cmesh_new_hybrid_gate_deformed, t8_cmesh_new_full_hybrid }; -std::vector names = { "t8_cmesh_new_periodic_tri_", - "t8_cmesh_new_periodic_hybrid_", - "t8_cmesh_new_periodic_line_more_trees_", - "t8_cmesh_new_line_zigzag_", - "t8_cmesh_new_prism_deformed_", - "t8_cmesh_new_pyramid_deformed_", - "t8_cmesh_new_prism_cake_funny_oriented_", - "t8_cmesh_new_prism_geometry_", - "t8_cmesh_new_tet_orientation_test_", - "t8_cmesh_new_hybrid_gate_", - "t8_cmesh_new_hybrid_gate_deformed_", - "t8_cmesh_new_full_hybrid_" }; +std::vector names = { "t8_cmesh_new_periodic_tri", + "t8_cmesh_new_periodic_hybrid", + "t8_cmesh_new_periodic_line_more_trees", + "t8_cmesh_new_line_zigzag", + "t8_cmesh_new_prism_deformed", + "t8_cmesh_new_pyramid_deformed", + "t8_cmesh_new_prism_cake_funny_oriented", + "t8_cmesh_new_prism_geometry", + "t8_cmesh_new_tet_orientation_test", + "t8_cmesh_new_hybrid_gate", + "t8_cmesh_new_hybrid_gate_deformed", + "t8_cmesh_new_full_hybrid" }; example_set *cmesh_example = (example_set *) new cmesh_cartesian_product_params ( diff --git a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_disjoint_bricks_param.hxx b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_disjoint_bricks_param.hxx index 4f2739deba..bf647edd8d 100644 --- a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_disjoint_bricks_param.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_disjoint_bricks_param.hxx @@ -60,7 +60,7 @@ example_set *cmesh_example = (example_set *) new cmesh_cartesian_product_params< std::make_pair (cmesh_params::periodic.begin (), cmesh_params::periodic.end ()), std::make_pair (cmesh_params::periodic.begin (), cmesh_params::periodic.end ()), std::make_pair (cmesh_params::my_comms.begin (), cmesh_params::my_comms.end ()), disjoint_bricks, - make_param_string_wrapper, "t8_cmesh_new_disjoint_brick_"); + make_param_string_wrapper, "t8_cmesh_new_disjoint_brick"); } // namespace new_disjoint_bricks #endif /* T8_CMESH_NEW_DISJOINT_BRICKS_PARAM_HXX */ diff --git a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_empty.hxx b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_empty.hxx index 8f6252e5e4..11273d995a 100644 --- a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_empty.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_empty.hxx @@ -52,7 +52,7 @@ example_set *cmesh_example std::make_pair (cmesh_params::my_comms.begin (), cmesh_params::my_comms.end ()), std::make_pair (cmesh_params::partition.begin (), cmesh_params::partition.end ()), std::make_pair (cmesh_params::do_bcast.begin (), cmesh_params::do_bcast.end ()), new_from_class_wrapper, - print_function, "t8_cmesh_new_empty_"); + print_function, "t8_cmesh_new_empty"); } /* namespace new_empty */ #endif /* T8_CMESH_NEW_EMPTY_HXX */ diff --git a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_from_class_param.hxx b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_from_class_param.hxx index 983739b2a4..7c0b30f15d 100644 --- a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_from_class_param.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_from_class_param.hxx @@ -49,7 +49,7 @@ example_set *cmesh_example decltype (cmesh_params::my_comms.begin ())> ( std::make_pair (cmesh_params::eclasses.begin (), cmesh_params::eclasses.end ()), std::make_pair (cmesh_params::my_comms.begin (), cmesh_params::my_comms.end ()), new_from_class_wrapper, - print_function, "t8_cmesh_new_from_class_"); + print_function, "t8_cmesh_new_from_class"); } // namespace new_from_class diff --git a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_hypercube_param.hxx b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_hypercube_param.hxx index 477cafc2de..84e40f6f39 100644 --- a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_hypercube_param.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_hypercube_param.hxx @@ -63,7 +63,7 @@ example_set *cmesh_example = (example_set *) new cmesh_cartesian_product_params< std::make_pair (cmesh_params::do_bcast.begin (), cmesh_params::do_bcast.end ()), std::make_pair (cmesh_params::partition.begin (), cmesh_params::partition.end ()), std::make_pair (cmesh_params::periodic.begin (), cmesh_params::periodic.end ()), cmesh_wrapper, param_to_string, - "t8_cmesh_new_hypercube_"); + "t8_cmesh_new_hypercube"); example_set *cmesh_example_pyra = (example_set *) new cmesh_cartesian_product_params< decltype (periodic_eclasses.begin ()), decltype (cmesh_params::my_comms.begin ()), @@ -74,7 +74,7 @@ example_set *cmesh_example_pyra = (example_set *) new cmesh_cartesian_product_pa std::make_pair (cmesh_params::do_bcast.begin (), cmesh_params::do_bcast.end ()), std::make_pair (cmesh_params::partition.begin (), cmesh_params::partition.end ()), std::make_pair (cmesh_params::no_periodic.begin (), cmesh_params::no_periodic.end ()), cmesh_wrapper, param_to_string, - "t8_cmesh_new_hypercube_"); + "t8_cmesh_new_hypercube"); } // namespace new_hypercube_cmesh #endif /* T8_CMESH_NEW_HYPERCUBE_PARAM_HXX */ diff --git a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_periodic.hxx b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_periodic.hxx index 5c022f4eda..e57aed1d13 100644 --- a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_periodic.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_periodic.hxx @@ -47,7 +47,7 @@ example_set *cmesh_example decltype (cmesh_params::dims.begin ())> ( std::make_pair (cmesh_params::my_comms.begin (), cmesh_params::my_comms.end ()), std::make_pair (cmesh_params::dims.begin (), cmesh_params::dims.end ()), new_from_periodic_wrapper, print_function, - "t8_cmesh_new_periodic_"); + "t8_cmesh_new_periodic"); } // namespace new_periodic #endif /* T8_CMESH_NEW_PERIODIC_HXX */ diff --git a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_prism_cake_param.hxx b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_prism_cake_param.hxx index 42e3319637..68c663da88 100644 --- a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_prism_cake_param.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_new_prism_cake_param.hxx @@ -46,7 +46,7 @@ example_set *cmesh_example decltype (cmesh_params::num_prisms.begin ())> ( std::make_pair (cmesh_params::my_comms.begin (), cmesh_params::my_comms.end ()), std::make_pair (cmesh_params::num_prisms.begin (), cmesh_params::num_prisms.end ()), prism_cake, - make_param_string_wrapper, "t8_cmesh_new_prism_cake_"); + make_param_string_wrapper, "t8_cmesh_new_prism_cake"); } // namespace new_prism_cake #endif /* T8_CMESH_NEW_PRISM_CAKE_PARAM_HXX */ diff --git a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_params.hxx b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_params.hxx index 53c42de2a5..cbed2eaa6f 100644 --- a/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_params.hxx +++ b/test/t8_cmesh_generator/t8_cmesh_parameterized_examples/t8_cmesh_params.hxx @@ -34,11 +34,7 @@ #include #include -#define T8_CMESH_TEST_NUM_COMMS 1 -#define T8_CMESH_BINARY 2 -#define T8_CMESH_DIM_RANGE 4 /* this is the dim range for hypercube hybrid and empty cmesh */ -#define T8_CMESH_MAX_TEST_DIMS 3 -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 #define T8_CMESH_MAX_NUM_OF_TREES 5 #define T8_CMESH_MAX_NUM_OF_PRISMS 5 #define T8_CMESH_MAX_NUM_XYZ_TREES 2 @@ -90,8 +86,6 @@ std::vector all_eclasses std::vector do_bcast = { 0, 1 }; std::vector partition = { 0, 1 }; -/* Currently a dummy vector for examples that have partition argument but not fully support it yet */ -std::vector no_partition = { 0 }; std::vector periodic = { 0, 1 }; /* Currently a dummy vector for examples that have periodic argument but not fully support it yet */ diff --git a/test/t8_forest/t8_gtest_balance.cxx b/test/t8_forest/t8_gtest_balance.cxx index 3f2e5c7d16..fa4ef734d4 100644 --- a/test/t8_forest/t8_gtest_balance.cxx +++ b/test/t8_forest/t8_gtest_balance.cxx @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -40,7 +41,7 @@ #include #include -class gtest_balance: public testing::TestWithParam> { +class gtest_balance: public testing::TestWithParam, int, int>> { public: static const int kNumTrees = 4; @@ -48,11 +49,14 @@ class gtest_balance: public testing::TestWithParam (GetParam ()); + const int scheme_id = std::get<0> (std::get<0> (GetParam ())); + scheme = create_from_scheme_id (scheme_id); + eclass = std::get<1> (std::get<0> (GetParam ())); ilevel = std::get<1> (GetParam ()); ido_periodic = std::get<2> (GetParam ()); } - t8_eclass_t ieclass; + t8_eclass_t eclass; + const t8_scheme *scheme; int ilevel; int ido_periodic; }; @@ -62,12 +66,12 @@ class gtest_balance: public testing::TestWithParamunref (); GTEST_SKIP_ ("The pyramid cube mesh cannot be periodic."); - - const t8_scheme *default_scheme = t8_scheme_new_default (); - t8_cmesh_t cmesh = t8_cmesh_new_hypercube (ieclass, sc_MPI_COMM_WORLD, 0, 0, ido_periodic); - t8_forest_t forest = t8_forest_new_uniform (cmesh, default_scheme, ilevel, 0, sc_MPI_COMM_WORLD); + } + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (eclass, sc_MPI_COMM_WORLD, 0, 0, ido_periodic); + t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, ilevel, 0, sc_MPI_COMM_WORLD); EXPECT_EQ (t8_forest_is_balanced (forest), 1); @@ -122,7 +126,7 @@ t8_gtest_balance_refine_certain_trees (t8_forest_t forest, t8_forest_t forest_fr * \return The adapted forest; as shown above */ static t8_forest_t -t8_gtest_obtain_forest_for_balance_tests (const std::vector &trees_to_refine, +t8_gtest_obtain_forest_for_balance_tests (const std::vector &trees_to_refine, const t8_scheme *scheme, int additional_refinement = 0) { const double boundary_coords[12] = { 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0 }; @@ -132,7 +136,7 @@ t8_gtest_obtain_forest_for_balance_tests (const std::vector &trees_ t8_forest_t forest; t8_forest_init (&forest); t8_forest_set_cmesh (forest, cmesh, sc_MPI_COMM_WORLD); - t8_forest_set_scheme (forest, t8_scheme_new_default ()); + t8_forest_set_scheme (forest, scheme); t8_forest_commit (forest); gtest_balance_adapt_data adapt_data; @@ -194,10 +198,10 @@ t8_gtest_check_custom_balanced_forest (t8_forest_t balanced_forest, * |__|__|__|__|__ __ __ __| |__|__|__|__|__ __|__ __| * */ -TEST (gtest_balance, balance_adapted_forest_no_repartition) +TEST_P (gtest_balance, balance_adapted_forest_no_repartition) { std::vector trees_to_refine { 0 }; - t8_forest_t forest = t8_gtest_obtain_forest_for_balance_tests (trees_to_refine); + t8_forest_t forest = t8_gtest_obtain_forest_for_balance_tests (trees_to_refine, scheme, 0); const int flag_no_repartition = 1; @@ -216,12 +220,12 @@ TEST (gtest_balance, balance_adapted_forest_no_repartition) /** * \brief Tests whether an already balanced forest remains unchanged after another balance iteration. */ -TEST (gtest_balance, balance_consistency_test) +TEST_P (gtest_balance, balance_consistency_test) { const int additional_refinement = 2; std::vector trees_to_refine { 1, 2 }; - t8_forest_t forest = t8_gtest_obtain_forest_for_balance_tests (trees_to_refine, additional_refinement); + t8_forest_t forest = t8_gtest_obtain_forest_for_balance_tests (trees_to_refine, scheme, additional_refinement); int flag_no_repartition = 0; t8_forest_t balanced_forest; @@ -243,5 +247,11 @@ TEST (gtest_balance, balance_consistency_test) t8_forest_unref (&already_balanced_forest); } +#if T8CODE_TEST_LEVEL == 1 +const int maxlvl = 4; +#else +const int maxlvl = 5; +#endif + INSTANTIATE_TEST_SUITE_P (t8_gtest_balance, gtest_balance, - testing::Combine (AllEclasses, testing::Range (0, 5), testing::Range (0, 2))); + testing::Combine (DefaultScheme, testing::Range (0, maxlvl), testing::Range (0, 2))); diff --git a/test/t8_forest/t8_gtest_element_is_leaf.cxx b/test/t8_forest/t8_gtest_element_is_leaf.cxx index e2475ca61e..5cb65d0817 100644 --- a/test/t8_forest/t8_gtest_element_is_leaf.cxx +++ b/test/t8_forest/t8_gtest_element_is_leaf.cxx @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -35,8 +36,12 @@ */ /* Maximum uniform level for forest. */ -#define T8_IS_LEAF_MAX_LVL 4 +#if T8CODE_TEST_LEVEL == 1 +#define T8_IS_LEAF_MAX_LVL 3 +#else +#define T8_IS_LEAF_MAX_LVL 4 +#endif /* Adapt a forest such that always the first child of a * family is refined and no other elements. This results in a highly * imbalanced forest. */ @@ -62,21 +67,22 @@ t8_test_adapt_first_child (t8_forest_t forest, t8_forest_t forest_from, t8_locid return 0; } -class element_is_leaf: public testing::TestWithParam> { +class element_is_leaf: public testing::TestWithParam> { protected: void SetUp () override { /* Construct a cmesh */ - const int level = std::get<0> (GetParam ()); - t8_cmesh_t cmesh = std::get<1> (GetParam ())->cmesh_create (); + const int scheme_id = std::get<0> (GetParam ()); + scheme = create_from_scheme_id (scheme_id); + const int level = std::get<1> (GetParam ()); + t8_cmesh_t cmesh = std::get<2> (GetParam ())->cmesh_create (); if (t8_cmesh_is_empty (cmesh)) { /* forest_commit does not support empty cmeshes, we skip this case */ + scheme->unref (); t8_cmesh_unref (&cmesh); GTEST_SKIP (); } - /* Build the default scheme (TODO: Test this with all schemes) */ - scheme = t8_scheme_new_default (); forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); t8_forest_ref (forest); //const int maxlevel = t8_forest_get_maxlevel (forest); @@ -148,14 +154,17 @@ TEST_P (element_is_leaf, element_is_leaf_adapt) /* Define a lambda to beatify gtest output for tuples . * This will set the correct level and cmesh name as part of the test case name. */ auto pretty_print_level_and_cmesh_params - = [] (const testing::TestParamInfo> &info) { - std::string name = std::string ("Level_") + std::to_string (std::get<0> (info.param)); + = [] (const testing::TestParamInfo> &info) { + std::string name = std::string ("Level_") + std::to_string (std::get<1> (info.param)); std::string cmesh_name; - std::get<1> (info.param)->param_to_string (cmesh_name); + std::get<2> (info.param)->param_to_string (cmesh_name); name += std::string ("_") + cmesh_name; + name += std::string ("scheme_") + std::to_string (std::get<0> (info.param)); + name += std::string ("_") + std::to_string (info.index); return name; }; INSTANTIATE_TEST_SUITE_P (t8_gtest_element_is_leaf, element_is_leaf, - testing::Combine (testing::Range (0, T8_IS_LEAF_MAX_LVL), AllCmeshsParam), + testing::Combine (AllSchemeCollections, testing::Range (0, T8_IS_LEAF_MAX_LVL), + AllCmeshsParam), pretty_print_level_and_cmesh_params); diff --git a/test/t8_forest/t8_gtest_element_volume.cxx b/test/t8_forest/t8_gtest_element_volume.cxx index 7cd5132b69..1e0ddb79ec 100644 --- a/test/t8_forest/t8_gtest_element_volume.cxx +++ b/test/t8_forest/t8_gtest_element_volume.cxx @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,14 +38,15 @@ /* Construct a forest of a hypercube with volume 1. If the element are refined uniformly * all elements have volume 1/global_num_elements. */ -class t8_forest_volume: public testing::TestWithParam> { +class t8_forest_volume: public testing::TestWithParam, int>> { protected: void SetUp () override { - eclass = std::get<0> (GetParam ()); + const int scheme_id = std::get<0> (std::get<0> (GetParam ())); + scheme = create_from_scheme_id (scheme_id); + eclass = std::get<1> (std::get<0> (GetParam ())); level = std::get<1> (GetParam ()); - scheme = t8_scheme_new_default (); t8_cmesh_t cmesh = t8_cmesh_new_hypercube (eclass, sc_MPI_COMM_WORLD, 0, 0, 0); forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); } @@ -118,4 +120,4 @@ TEST_P (t8_forest_volume, volume_check) } INSTANTIATE_TEST_SUITE_P (t8_gtest_element_volume, t8_forest_volume, - testing::Combine (AllEclasses, testing::Range (0, 4))); + testing::Combine (DefaultScheme, testing::Range (0, 4))); diff --git a/test/t8_forest/t8_gtest_find_owner.cxx b/test/t8_forest/t8_gtest_find_owner.cxx index e4d950e231..f7d4fecdad 100644 --- a/test/t8_forest/t8_gtest_find_owner.cxx +++ b/test/t8_forest/t8_gtest_find_owner.cxx @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -33,20 +34,20 @@ #include #include -class forest_find_owner: public testing::TestWithParam { +class forest_find_owner: public testing::TestWithParam> { protected: void SetUp () override { - tree_class = GetParam (); - - default_scheme = t8_scheme_new_default (); + const int scheme_id = std::get<0> (GetParam ()); + scheme = create_from_scheme_id (scheme_id); + tree_class = std::get<1> (GetParam ()); /* Construct a coarse mesh of one tree */ cmesh = t8_cmesh_new_from_class (tree_class, sc_MPI_COMM_WORLD); } t8_eclass_t tree_class; t8_cmesh_t cmesh; - const t8_scheme *default_scheme; + const t8_scheme *scheme; }; #if 0 @@ -95,10 +96,10 @@ TEST_P (forest_find_owner, find_owner) /* build the cmesh */ cmesh = t8_test_create_cmesh (itype, tree_class, sc_MPI_COMM_WORLD); /* We reuse the scheme for all forests and thus ref it */ - t8_scheme_ref (default_scheme); + t8_scheme_ref (scheme); /* build the forest */ t8_forest_t forest = - t8_forest_new_uniform (cmesh, default_scheme, level, 0, + t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); for (int itree = 0, t8_gloidx_t global_elem_num = 0; itree < t8_forest_get_num_global_trees (forest); itree++) { @@ -130,7 +131,7 @@ TEST_P (forest_find_owner, find_owner) } /* clean-up */ scheme->element_destroy (tree_class, 1, &element); - t8_scheme_unref (&default_scheme); + t8_scheme_unref (&scheme); } #endif @@ -144,7 +145,7 @@ TEST_P (forest_find_owner, find_multiple_owners) /* initialize the array of owners to store ints */ sc_array_init (&owners, sizeof (int)); /* Build a uniform forest */ - t8_forest_t forest = t8_forest_new_uniform (cmesh, default_scheme, level, 0, sc_MPI_COMM_WORLD); + t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); const t8_scheme *scheme = t8_forest_get_scheme (forest); /* Construct the root element */ scheme->element_new (tree_class, 1, &root_element); @@ -169,4 +170,4 @@ TEST_P (forest_find_owner, find_multiple_owners) sc_array_reset (&owners); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_find_owner, forest_find_owner, AllEclasses, print_eclass); +INSTANTIATE_TEST_SUITE_P (t8_gtest_find_owner, forest_find_owner, DefaultScheme); diff --git a/test/t8_forest/t8_gtest_forest_commit.cxx b/test/t8_forest/t8_gtest_forest_commit.cxx index b2d0b7c986..349f4d1149 100644 --- a/test/t8_forest/t8_gtest_forest_commit.cxx +++ b/test/t8_forest/t8_gtest_forest_commit.cxx @@ -29,6 +29,7 @@ #include #include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" #include +#include /* In this test we adapt, balance and partition a uniform forest. * We do this in two ways: @@ -38,15 +39,18 @@ * After these two forests are created, we check for equality. */ -class forest_commit: public testing::TestWithParam { +class forest_commit: public testing::TestWithParam> { protected: void SetUp () override { + const int scheme_id = std::get<0> (GetParam ()); + scheme = create_from_scheme_id (scheme_id); /* Construct a cmesh */ - cmesh = GetParam ()->cmesh_create (); + cmesh = std::get<1> (GetParam ())->cmesh_create (); if (t8_cmesh_is_empty (cmesh)) { /* forest_commit does not support empty cmeshes*/ + scheme->unref (); GTEST_SKIP (); } } @@ -56,6 +60,7 @@ class forest_commit: public testing::TestWithParam { t8_cmesh_destroy (&cmesh); } t8_cmesh_t cmesh; + const t8_scheme *scheme; }; /* Adapt a forest such that always the first child of a @@ -122,7 +127,7 @@ t8_test_forest_commit_abp_3step (t8_forest_t forest, int maxlevel) t8_forest_set_balance (forest_balance, forest_adapt, 0); t8_forest_commit (forest_balance); - /* partrition the forest */ + /* partition the forest */ t8_forest_set_partition (forest_partition, forest_balance, 0); t8_forest_commit (forest_partition); @@ -138,8 +143,6 @@ TEST_P (forest_commit, test_forest_commit) const int level_step = 2; - const t8_scheme *scheme = t8_scheme_new_default (); - /* Compute the first level, such that no process is empty */ int min_level = t8_forest_min_nonempty_level (cmesh, scheme); /* Use one level with empty processes */ @@ -167,4 +170,5 @@ TEST_P (forest_commit, test_forest_commit) t8_debugf ("Done testing forest commit."); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_forest_commit, forest_commit, AllCmeshsParam, pretty_print_base_example); +INSTANTIATE_TEST_SUITE_P (t8_gtest_forest_commit, forest_commit, + testing::Combine (AllSchemeCollections, AllCmeshsParam), pretty_print_base_example_scheme); diff --git a/test/t8_forest/t8_gtest_forest_face_normal.cxx b/test/t8_forest/t8_gtest_forest_face_normal.cxx index 2b83917755..09ba93a540 100644 --- a/test/t8_forest/t8_gtest_forest_face_normal.cxx +++ b/test/t8_forest/t8_gtest_forest_face_normal.cxx @@ -29,19 +29,21 @@ #include #include #include +#include /** * This file tests the face normal computation of elements. */ -class class_forest_face_normal: public testing::TestWithParam> { +class class_forest_face_normal: public testing::TestWithParam, int>> { protected: void SetUp () override { - eclass = std::get<0> (GetParam ()); + const int scheme_id = std::get<0> (std::get<0> (GetParam ())); + scheme = create_from_scheme_id (scheme_id); + eclass = std::get<1> (std::get<0> (GetParam ())); level = std::get<1> (GetParam ()); - scheme = t8_scheme_new_default (); t8_cmesh_t cmesh = t8_cmesh_new_hypercube (eclass, sc_MPI_COMM_WORLD, 0, 0, 0); const int do_face_ghost = 1; forest = t8_forest_new_uniform (cmesh, scheme, level, do_face_ghost, sc_MPI_COMM_WORLD); @@ -113,4 +115,4 @@ TEST_P (class_forest_face_normal, back_and_forth) } INSTANTIATE_TEST_SUITE_P (t8_gtest_forest_face_normal, class_forest_face_normal, - testing::Combine (AllEclasses, testing::Range (0, 2))); + testing::Combine (DefaultScheme, testing::Range (0, 2))); diff --git a/test/t8_forest/t8_gtest_ghost_and_owner.cxx b/test/t8_forest/t8_gtest_ghost_and_owner.cxx index 0ce758557a..1d7cbe628e 100644 --- a/test/t8_forest/t8_gtest_ghost_and_owner.cxx +++ b/test/t8_forest/t8_gtest_ghost_and_owner.cxx @@ -29,6 +29,7 @@ #include #include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" #include +#include /* This test program tests the forest ghost layer. * We adapt a forest and create its ghost layer. Afterwards, we @@ -36,15 +37,15 @@ * element is in face the owner that is stored in the ghost layer. */ -class forest_ghost_owner: public testing::TestWithParam { +class forest_ghost_owner: public testing::TestWithParam> { protected: void SetUp () override { - - scheme = t8_scheme_new_default (); + const int scheme_id = std::get<0> (GetParam ()); + scheme = create_from_scheme_id (scheme_id); /* Construct a cmesh */ - cmesh = GetParam ()->cmesh_create (); + cmesh = std::get<1> (GetParam ())->cmesh_create (); if (t8_cmesh_is_empty (cmesh)) { /* empty cmeshes are currently not supported */ GTEST_SKIP (); @@ -145,4 +146,5 @@ TEST_P (forest_ghost_owner, test_ghost_owner) } } -INSTANTIATE_TEST_SUITE_P (t8_gtest_ghost_and_owner, forest_ghost_owner, AllCmeshsParam, pretty_print_base_example); +INSTANTIATE_TEST_SUITE_P (t8_gtest_ghost_and_owner, forest_ghost_owner, + testing::Combine (AllSchemeCollections, AllCmeshsParam), pretty_print_base_example_scheme); diff --git a/test/t8_forest/t8_gtest_ghost_delete.cxx b/test/t8_forest/t8_gtest_ghost_delete.cxx index 1cecbd8df4..10957cce84 100644 --- a/test/t8_forest/t8_gtest_ghost_delete.cxx +++ b/test/t8_forest/t8_gtest_ghost_delete.cxx @@ -28,6 +28,7 @@ #include #include #include +#include /* This test is executed on a subcommunicator of exactly 2 procs, because it demonstrates a configuration that is currently not working. See https://github.com/DLR-AMR/t8code/issues/825. * A partitioned square of uniform refinement level 1 is adapted once, where only the lower half is refined. @@ -54,7 +55,7 @@ test_adapt_holes (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which return 0; } -class DISABLED_forest_ghost_exchange_holes: public testing::Test { +class DISABLED_forest_ghost_exchange_holes: public testing::TestWithParam { protected: void SetUp () override @@ -79,7 +80,8 @@ class DISABLED_forest_ghost_exchange_holes: public testing::Test { if (comm != sc_MPI_COMM_NULL) { sc_MPI_Comm_size (comm, &size); T8_ASSERT (size <= 2); - scheme = t8_scheme_new_default (); + const int scheme_id = GetParam (); + scheme = create_from_scheme_id (scheme_id); /* Construct a cmesh */ cmesh = t8_cmesh_new_hypercube (T8_ECLASS_QUAD, comm, 0, 0, 0); } @@ -103,7 +105,7 @@ class DISABLED_forest_ghost_exchange_holes: public testing::Test { t8_cmesh_t cmesh; }; -TEST_F (DISABLED_forest_ghost_exchange_holes, errorTest) +TEST_P (DISABLED_forest_ghost_exchange_holes, errorTest) { /* This test tests the functionality described in Issue: https://github.com/DLR-AMR/t8code/issues/825 */ @@ -118,3 +120,5 @@ TEST_F (DISABLED_forest_ghost_exchange_holes, errorTest) t8_forest_unref (&forest); } } + +INSTANTIATE_TEST_SUITE_P (t8_gtest_ghost_delete, DISABLED_forest_ghost_exchange_holes, AllSchemeCollections); diff --git a/test/t8_forest/t8_gtest_ghost_exchange.cxx b/test/t8_forest/t8_gtest_ghost_exchange.cxx index bbe93e2d43..31c9daa7c3 100644 --- a/test/t8_forest/t8_gtest_ghost_exchange.cxx +++ b/test/t8_forest/t8_gtest_ghost_exchange.cxx @@ -29,8 +29,7 @@ #include #include "test/t8_cmesh_generator/t8_cmesh_example_sets.hxx" #include - -/* TODO: when this test works for all cmeshes remove if statement in test_cmesh_ghost_exchange_all () */ +#include /* This test program tests the forest ghost exchange routine. * Given a forest for which the ghost layer was created and an array @@ -43,14 +42,15 @@ * in a second test, we store the element's linear id in the data array. */ -class forest_ghost_exchange: public testing::TestWithParam { +class forest_ghost_exchange: public testing::TestWithParam> { protected: void SetUp () override { - scheme = t8_scheme_new_default (); + const int scheme_id = std::get<0> (GetParam ()); + scheme = create_from_scheme_id (scheme_id); /* Construct a cmesh */ - cmesh = GetParam ()->cmesh_create (); + cmesh = std::get<1> (GetParam ())->cmesh_create (); if (t8_cmesh_is_empty (cmesh)) { /* empty cmeshes are currently not supported */ GTEST_SKIP (); @@ -195,4 +195,5 @@ TEST_P (forest_ghost_exchange, test_ghost_exchange) } } -INSTANTIATE_TEST_SUITE_P (t8_gtest_ghost_exchange, forest_ghost_exchange, AllCmeshsParam, pretty_print_base_example); +INSTANTIATE_TEST_SUITE_P (t8_gtest_ghost_exchange, forest_ghost_exchange, + testing::Combine (AllSchemeCollections, AllCmeshsParam), pretty_print_base_example_scheme); diff --git a/test/t8_forest/t8_gtest_half_neighbors.cxx b/test/t8_forest/t8_gtest_half_neighbors.cxx index ba55170bda..2a752361c5 100644 --- a/test/t8_forest/t8_gtest_half_neighbors.cxx +++ b/test/t8_forest/t8_gtest_half_neighbors.cxx @@ -32,17 +32,17 @@ #include #include #include -#include +#include -class forest_half_neighbors: public testing::TestWithParam> { +class forest_half_neighbors: public testing::TestWithParam, int>> { protected: void SetUp () override { - eclass = std::get<0> (GetParam ()); + const int scheme_id = std::get<0> (std::get<0> (GetParam ())); + scheme = create_from_scheme_id (scheme_id); + eclass = std::get<1> (std::get<0> (GetParam ())); cmesh_type = std::get<1> (GetParam ()); - - default_scheme = t8_scheme_new_default (); /* Construct a coarse mesh of one tree */ cmesh = t8_cmesh_new_from_class (eclass, sc_MPI_COMM_WORLD); } @@ -50,7 +50,7 @@ class forest_half_neighbors: public testing::TestWithParam #include #include +#include #include #include @@ -45,6 +46,7 @@ * function \see TestPartitionData. */ class t8_test_partition_data_t { + public: t8_test_partition_data_t () = default; t8_test_partition_data_t (const t8_gloidx_t value): data { value } {}; @@ -211,6 +213,19 @@ t8_test_partition_data_adapt (t8_forest_t forest, t8_forest_t forest_from, t8_lo } } +class t8_test_partition_data_test: public testing::TestWithParam> { + + protected: + void + SetUp () override + { + const int scheme_id = std::get<0> (GetParam ()); + scheme = create_from_scheme_id (scheme_id); + eclass = std::get<1> (GetParam ()); + } + const t8_scheme* scheme; + t8_eclass_t eclass; +}; /** * \brief Construct a new TEST object for the t8_forest_partition_data functionality. * The tests constructs a hypercube forest of the triangle class in which the first tree is refined @@ -221,11 +236,10 @@ t8_test_partition_data_adapt (t8_forest_t forest, t8_forest_t forest_from, t8_lo * partition given by the partitioned forest. * At last the data is checked for compliance with the partition of the partitioned forest. */ -TEST (partition_data, test_partition_data) +TEST_P (t8_test_partition_data_test, test_partition_data) { /* Build a forest */ - t8_cmesh_t cmesh = t8_cmesh_new_hypercube (T8_ECLASS_TRIANGLE, sc_MPI_COMM_WORLD, 0, 0, 0); - const t8_scheme* scheme = t8_scheme_new_default (); + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (eclass, sc_MPI_COMM_WORLD, 0, 0, 0); t8_forest_t base_forest = t8_forest_new_uniform (cmesh, scheme, 1, 0, sc_MPI_COMM_WORLD); /* Adapt the forest examplary. */ @@ -251,3 +265,5 @@ TEST (partition_data, test_partition_data) t8_forest_unref (&initial_forest); t8_forest_unref (&partitioned_forest); } + +INSTANTIATE_TEST_SUITE_P (t8_gtest_partititon_data, t8_test_partition_data_test, DefaultScheme); diff --git a/test/t8_forest/t8_gtest_search.cxx b/test/t8_forest/t8_gtest_search.cxx index 884d8f6795..1edaa40e62 100644 --- a/test/t8_forest/t8_gtest_search.cxx +++ b/test/t8_forest/t8_gtest_search.cxx @@ -26,23 +26,25 @@ #include #include #include -#include +#include #include #include +#include -class forest_search: public testing::TestWithParam> { +class forest_search: public testing::TestWithParam, int>> { protected: void SetUp () override { - eclass = std::get<0> (GetParam ()); + const int scheme_id = std::get<0> (std::get<0> (GetParam ())); + scheme = create_from_scheme_id (scheme_id); + eclass = std::get<1> (std::get<0> (GetParam ())); level = std::get<1> (GetParam ()); - default_scheme = t8_scheme_new_default (); /* Construct a cube coarse mesh */ cmesh = t8_cmesh_new_hypercube (eclass, sc_MPI_COMM_WORLD, 0, 0, 0); /* Build a uniform forest */ - forest = t8_forest_new_uniform (cmesh, default_scheme, level, 0, sc_MPI_COMM_WORLD); + forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); } void TearDown () override @@ -52,7 +54,7 @@ class forest_search: public testing::TestWithParam> { int level; t8_cmesh_t cmesh; t8_forest_t forest; - const t8_scheme *default_scheme; + const t8_scheme *scheme; }; /* A search function that matches all elements. @@ -60,92 +62,100 @@ class forest_search: public testing::TestWithParam> { * with one int for each local leaf. * If this function is called for a leaf, it sets the corresponding entry to 1. */ -static int -t8_test_search_all_fn (t8_forest_t forest, const t8_locidx_t ltreeid, const t8_element_t *element, const int is_leaf, - const t8_element_array_t *leaf_elements, const t8_locidx_t tree_leaf_index) +bool +t8_test_search_all_fn (const t8_forest_t forest, const t8_locidx_t ltreeid, const t8_element_t *element, + const bool is_leaf, const t8_element_array_t *leaf_elements, const t8_locidx_t tree_leaf_index, + std::vector *user_data) { - sc_array_t *matched_leaves = (sc_array_t *) t8_forest_get_user_data (forest); + T8_ASSERT (t8_forest_is_committed (forest)); + T8_ASSERT (user_data != nullptr); if (is_leaf) { - t8_locidx_t test_ltreeid; const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, ltreeid); - const t8_scheme *scheme = t8_forest_get_scheme (forest); - + const t8_scheme *ts = t8_forest_get_scheme (forest); const t8_locidx_t tree_offset = t8_forest_get_tree_element_offset (forest, ltreeid); /* Set the corresponding entry to 1 */ - *(int *) t8_sc_array_index_locidx (matched_leaves, tree_offset + tree_leaf_index) = 1; + (*user_data)[tree_offset + tree_leaf_index] = true; /* Test whether tree_leaf_index is actually the index of the element */ + t8_locidx_t test_ltreeid; const t8_element_t *test_element = t8_forest_get_element (forest, tree_offset + tree_leaf_index, &test_ltreeid); - EXPECT_ELEM_EQ (scheme, tree_class, element, test_element); + EXPECT_ELEM_EQ (ts, tree_class, element, test_element); EXPECT_EQ (ltreeid, test_ltreeid) << "Tree mismatch in search."; } - return 1; + return true; } -static void -t8_test_search_query_all_fn (t8_forest_t forest, t8_locidx_t ltreeid, const t8_element_t *element, const int is_leaf, - const t8_element_array_t *leaf_elements, const t8_locidx_t tree_leaf_index, - sc_array_t *queries, sc_array_t *query_indices, int *query_matches, - const size_t num_active_queries) +inline bool +t8_test_search_query_all_fn (const t8_forest_t forest, const t8_locidx_t ltreeid, const t8_element_t *element, + const bool is_leaf, const t8_element_array_t *leaf_elements, + const t8_locidx_t tree_leaf_index, const int &querie, std::vector *user_data) { - EXPECT_TRUE (queries != NULL) << "query callback must be called with queries argument. "; - EXPECT_EQ (num_active_queries, (long unsigned int) 1) << "Wrong number of active queries passed to query callback."; - for (size_t iquery = 0; iquery < num_active_queries; iquery++) { - void *query = sc_array_index_int (queries, iquery); - /* The query callback is always called with a query */ - EXPECT_TRUE (query != NULL) << "query " << iquery << " is NULL."; - /* The query is an int with value 42 (see below) */ - EXPECT_EQ (*(int *) query, 42) << "Wrong query argument passed to query callback."; - if (is_leaf) { - /* Test whether tree_leaf_index is actually the index of the element */ - t8_locidx_t test_ltreeid; - const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, ltreeid); - const t8_scheme *scheme = t8_forest_get_scheme (forest); - - t8_locidx_t tree_offset = t8_forest_get_tree_element_offset (forest, ltreeid); - t8_element_t *test_element = t8_forest_get_element (forest, tree_offset + tree_leaf_index, &test_ltreeid); - EXPECT_ELEM_EQ (scheme, tree_class, element, test_element); - EXPECT_EQ (ltreeid, test_ltreeid) << "Tree mismatch in search."; - } - query_matches[iquery] = 1; + /* The query is an int with value 42 (see below) */ + EXPECT_EQ (querie, 42) << "Wrong query argument passed to query callback."; + if (is_leaf) { + /* Test whether tree_leaf_index is actually the index of the element */ + t8_locidx_t test_ltreeid; + const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, ltreeid); + const t8_scheme *ts = t8_forest_get_scheme (forest); + + const t8_locidx_t tree_offset = t8_forest_get_tree_element_offset (forest, ltreeid); + const t8_element_t *test_element = t8_forest_get_element (forest, tree_offset + tree_leaf_index, &test_ltreeid); + EXPECT_ELEM_EQ (ts, tree_class, element, test_element); + EXPECT_EQ (ltreeid, test_ltreeid) << "Tree mismatch in search."; } + return true; } -TEST_P (forest_search, test_search_one_query_matches_all) +TEST_P (forest_search, t8_test_search_all_fn) { - const int query = 42; - sc_array_t queries; - sc_array_t matched_leaves; + t8_locidx_t num_elements = t8_forest_get_local_num_elements (forest); + /* set up an array in which we flag whether an element was matched in the + * search */ + std::vector matched_leaves (num_elements, false); + /* Call search. This search matches all elements. After this call we expect + * all entries in the matched_leaves array to be set to 1. */ + + t8_search> search (t8_test_search_all_fn); + + search.update_user_data (&matched_leaves); + search.update_forest (forest); + search.do_search (); + + /* Check whether matched_leaves entries are all 1 */ + for (size_t i = 0; i < matched_leaves.size (); ++i) { + ASSERT_TRUE (matched_leaves[i]) << "Search did not match all leaves. Mismatch at leaf " << i; + } + + t8_forest_unref (&forest); +} + +TEST_P (forest_search, test_search_one_query_matches_all) +{ /* set up a single query containing our query */ - sc_array_init_size (&queries, sizeof (int), 1); - *(int *) sc_array_index (&queries, 0) = query; + std::vector queries = { 42 }; t8_locidx_t num_elements = t8_forest_get_local_num_elements (forest); /* set up an array in which we flag whether an element was matched in the * search */ - sc_array_init_size (&matched_leaves, sizeof (int), num_elements); - /* write 0 in every entry */ - for (t8_locidx_t ielement = 0; ielement < num_elements; ++ielement) { - *(int *) t8_sc_array_index_locidx (&matched_leaves, ielement) = 0; - } + std::vector matched_leaves (num_elements, false); - /* Set the array as user data so that we can access it in the search callback */ - t8_forest_set_user_data (forest, &matched_leaves); /* Call search. This search matches all elements. After this call we expect * all entries in the matched_leaves array to be set to 1. */ - t8_forest_search (forest, t8_test_search_all_fn, t8_test_search_query_all_fn, &queries); + t8_search_with_queries> search (t8_test_search_all_fn, t8_test_search_query_all_fn, queries); + + search.update_queries (queries); + search.update_user_data (&matched_leaves); + search.update_forest (forest); + search.do_search (); /* Check whether matched_leaves entries are all 1 */ - for (t8_locidx_t ielement = 0; ielement < num_elements; ++ielement) { - ASSERT_TRUE (*(int *) t8_sc_array_index_locidx (&matched_leaves, ielement)) - << "Search did not match all leaves. First mismatch at leaf " << ielement; + for (size_t i = 0; i < matched_leaves.size (); ++i) { + ASSERT_TRUE (matched_leaves[i]) << "Search did not match all leaves. Mismatch at leaf " << i; } t8_forest_unref (&forest); - sc_array_reset (&matched_leaves); - sc_array_reset (&queries); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_search, forest_search, testing::Combine (AllEclasses, testing::Range (0, 6))); +INSTANTIATE_TEST_SUITE_P (t8_gtest_search, forest_search, testing::Combine (DefaultScheme, testing::Range (0, 6))); diff --git a/test/t8_forest/t8_gtest_transform.cxx b/test/t8_forest/t8_gtest_transform.cxx index 736cbb3f79..d1e1966a3a 100644 --- a/test/t8_forest/t8_gtest_transform.cxx +++ b/test/t8_forest/t8_gtest_transform.cxx @@ -37,17 +37,19 @@ #include #include #include +#include -class forest_transform: public testing::TestWithParam> { +class forest_transform: public testing::TestWithParam, int>> { protected: void SetUp () override { - tree_class = std::get<0> (GetParam ()); + const int scheme_id = std::get<0> (std::get<0> (GetParam ())); + scheme = create_from_scheme_id (scheme_id); + tree_class = std::get<1> (std::get<0> (GetParam ())); level = std::get<1> (GetParam ()); t8_debugf ("\n\n\nTesting eclass %s with level %i", t8_eclass_to_string[tree_class], level); - default_scheme = t8_scheme_new_default (); /* Construct a coarse mesh of one tree */ cmesh = t8_cmesh_new_from_class (tree_class, sc_MPI_COMM_WORLD); @@ -55,7 +57,7 @@ class forest_transform: public testing::TestWithParam t8_forest_init (&forest); t8_forest_set_level (forest, level); t8_forest_set_cmesh (forest, cmesh, sc_MPI_COMM_WORLD); - t8_forest_set_scheme (forest, default_scheme); + t8_forest_set_scheme (forest, scheme); t8_forest_commit (forest); } void @@ -65,7 +67,7 @@ class forest_transform: public testing::TestWithParam } t8_eclass_t tree_class; t8_cmesh_t cmesh; - const t8_scheme *default_scheme; + const t8_scheme *scheme; t8_forest_t forest; int level; }; @@ -170,10 +172,11 @@ TEST_P (forest_transform, test_forest_transform_elements) /* Get a pointer to the element */ t8_element_t *element = t8_forest_get_element (forest, ielem, NULL); /* perform the transform test */ - t8_test_transform_element (default_scheme, element, tree_class); + t8_test_transform_element (scheme, element, tree_class); } } INSTANTIATE_TEST_SUITE_P (t8_gtest_forest_transform, forest_transform, - testing::Combine (testing::Range (T8_ECLASS_QUAD, T8_ECLASS_TRIANGLE), + testing::Combine (::testing::Combine (AllSchemeCollections, + ::testing::Range (T8_ECLASS_QUAD, T8_ECLASS_TRIANGLE)), testing::Range (0, 6))); diff --git a/test/t8_forest/t8_gtest_user_data.cxx b/test/t8_forest/t8_gtest_user_data.cxx index bc8b01a410..177c76b143 100644 --- a/test/t8_forest/t8_gtest_user_data.cxx +++ b/test/t8_forest/t8_gtest_user_data.cxx @@ -29,16 +29,27 @@ #include #include #include +#include /* Test t8_forest_set/get_user_data. * We build a forest and set user data for it. * We then retrieve the data and check whether it is the same. */ -TEST (user_data, test_user_data) +class forest_user_data: public testing::TestWithParam { + protected: + void + SetUp () override + { + const int scheme_id = GetParam (); + scheme = create_from_scheme_id (scheme_id); + } + const t8_scheme *scheme; +}; + +TEST_P (forest_user_data, test_user_data) { /* Build a forest */ t8_cmesh_t cmesh = t8_cmesh_new_hypercube (T8_ECLASS_TRIANGLE, sc_MPI_COMM_WORLD, 0, 0, 0); - const t8_scheme *scheme = t8_scheme_new_default (); t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, 1, 0, sc_MPI_COMM_WORLD); /* Define user data */ double data = 42.42; @@ -92,11 +103,10 @@ t8_test_function_second (void) * We build a forest and set a user function for it. * We then retrieve the function and check whether it is the same. */ -TEST (user_data, test_user_function) +TEST_P (forest_user_data, test_user_function) { /* Build a forest */ t8_cmesh_t cmesh = t8_cmesh_new_hypercube (T8_ECLASS_TRIANGLE, sc_MPI_COMM_WORLD, 0, 0, 0); - const t8_scheme *scheme = t8_scheme_new_default (); t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, 1, 0, sc_MPI_COMM_WORLD); double (*funpointer) (int); @@ -123,3 +133,5 @@ TEST (user_data, test_user_function) /* clean up */ t8_forest_unref (&forest); } + +INSTANTIATE_TEST_SUITE_P (t8_gtest_ghost_user_data, forest_user_data, AllSchemeCollections); diff --git a/test/t8_forest_incomplete/t8_gtest_iterate_replace.cxx b/test/t8_forest_incomplete/t8_gtest_iterate_replace.cxx index cc43d418f7..75c7727d7c 100644 --- a/test/t8_forest_incomplete/t8_gtest_iterate_replace.cxx +++ b/test/t8_forest_incomplete/t8_gtest_iterate_replace.cxx @@ -117,7 +117,7 @@ t8_forest_replace (t8_forest_t forest_old, t8_forest_t forest_new, t8_locidx_t w ASSERT_EQ (num_outgoing, family_size); /* End check family */ - /* If element got coarsen, only the first element + /* If element got coarsened, only the first element * should be called in the callback of forest_adapt. */ for (t8_locidx_t i = 1; i < num_outgoing; i++) { ASSERT_EQ (adapt_data->callbacks[elidx_old + i], -3); @@ -138,7 +138,7 @@ t8_forest_replace (t8_forest_t forest_old, t8_forest_t forest_new, t8_locidx_t w } } -/** For each locale element: Remove, coarsen, leave untouched, or refine it depending on its index. +/** For each local element: Remove, coarsen, leave untouched, or refine it depending on its index. * if \a lelement_id mod 12 < 3 -> leave element untouched * else if \a lelement_id mod 12 < 6 -> coarse element * else if \a lelement_id mod 12 < 9 -> remove element @@ -220,7 +220,7 @@ t8_adapt_forest (t8_forest_t forest_from, t8_forest_adapt_t adapt_fn, int do_ada TEST_P (forest_iterate, test_iterate_replace) { -#if T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int runs = 1; #else const int runs = 2; diff --git a/test/t8_forest_incomplete/t8_gtest_permute_hole.cxx b/test/t8_forest_incomplete/t8_gtest_permute_hole.cxx index 7ee130f663..628a9d7cd3 100644 --- a/test/t8_forest_incomplete/t8_gtest_permute_hole.cxx +++ b/test/t8_forest_incomplete/t8_gtest_permute_hole.cxx @@ -65,7 +65,7 @@ class forest_permute: public testing::TestWithParam { SetUp () override { eclass = GetParam (); -#if T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 level = 1; #else level = eclass < 4 ? 2 : 1; diff --git a/test/t8_forest_incomplete/t8_gtest_recursive.cxx b/test/t8_forest_incomplete/t8_gtest_recursive.cxx index 9cc11dbbc1..95eca74ff2 100644 --- a/test/t8_forest_incomplete/t8_gtest_recursive.cxx +++ b/test/t8_forest_incomplete/t8_gtest_recursive.cxx @@ -155,4 +155,4 @@ TEST_P (recursive_tree, test_recursive) ASSERT_TRUE (t8_forest_is_equal (forest, forest_base)); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_recursive, recursive_tree, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_recursive, recursive_tree, AllSchemes, print_all_schemes); diff --git a/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_cad.cxx b/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_cad.cxx index 9851a0953e..42e47423b4 100644 --- a/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_cad.cxx +++ b/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_cad.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -256,11 +257,11 @@ t8_create_cad_hypercube (double *rot_vec, int face, int edge, double *parameters */ void t8_test_geometry_cad_hex (double *rot_vec, int face, int edge, double *parameters, double *test_ref_coords, - double *test_return_coords) + const t8_vec<24> &test_return_coords) { #if T8_WITH_OCC const int num_coords = 8; /* Number of reference coordinates to test */ - double *out_coords = T8_ALLOC (double, num_coords * 3); + t8_vec out_coords; double rotated_test_ref_coords[24]; double rotation_origin[3] = { 0.5, 0.5, 0.5 }; double inversed_rot_vec[3]; @@ -272,11 +273,9 @@ t8_test_geometry_cad_hex (double *rot_vec, int face, int edge, double *parameter } t8_euler_rotation (test_ref_coords, inversed_rot_vec, rotated_test_ref_coords, rotation_origin, num_coords); for (size_t coord = 0; coord < num_coords; ++coord) { - const int offset_3d = coord * 3; - t8_geometry_evaluate (cmesh, 0, rotated_test_ref_coords, num_coords, out_coords); - EXPECT_VEC3_EQ (out_coords + offset_3d, test_return_coords + offset_3d, tol); + t8_geometry_evaluate (cmesh, 0, rotated_test_ref_coords, num_coords, out_coords.data ()); + EXPECT_VEC_EQ (out_coords, test_return_coords, tol); } - T8_FREE (out_coords); t8_cmesh_destroy (&cmesh); #else /* !T8_WITH_OCC */ @@ -296,14 +295,14 @@ TEST (t8_gtest_geometry_cad_hex, linked_faces) 0.9, 0.25, 0.95, 0.1, 0.9, 0.9, 0.95, 0.85, 0.8 }; - double surface_test_return_coords[24] = { 0.0396282769, 0.1897542602, 0.0396282769, + const t8_vec<24> surface_test_return_coords ({ 0.0396282769, 0.1897542602, 0.0396282769, 0.8553975402, 0.1510451803, -0.0012778561, 0.1434278361, 0.9117760771, 0.0909403721, 0.9149739120, 0.8893780561, 0.2953610950, 0.2190065733, 0.1000000000, 0.7809934267, 0.9318450385, 0.1898146343, 0.9989190836, 0.0932920308, 0.9000000000, 0.9067079692, - 0.9673042609, 0.8312979801, 0.8063743210 }; + 0.9673042609, 0.8312979801, 0.8063743210 }); /* clang-format off */ double surface_rot_vecs[18] = { @@ -339,14 +338,14 @@ TEST (t8_gtest_geometry_cad_hex, linked_edges) 0.9, 0.25, 0.95, 0.1, 0.9, 0.9, 0.95, 0.85, 0.8 }; - double curve_test_return_coords[24] = { 0.0955204602, 0.2235162028, 0.1217553783, + const t8_vec<24> curve_test_return_coords ({ 0.0955204602, 0.2235162028, 0.1217553783, 0.7995278713, -0.0659838746, 0.2083328730, 0.1494299582, 0.9170222805, 0.1069555502, 0.8999105642, 0.8892289094, 0.3015732294, 0.2987855815, 0.1481519479, 0.7726155646, 0.8999520880, 0.2442297729, 0.9508428015, 0.0999446970, 0.9015248914, 0.9002685849, - 0.9499697383, 0.8472575225, 0.7998496263 }; + 0.9499697383, 0.8472575225, 0.7998496263 }); /* clang-format on */ double curve_rot_vecs[36] = { @@ -460,14 +459,18 @@ t8_create_cad_reference_tet (int face, int edge, double *parameters) * \param [in] comm The mpi communicator to use. * \return Returns 1 if passed, 0 if failed. */ +template void -t8_test_geometry_cad_tet (int face, int edge, double *parameters, double *test_ref_coords, double *test_return_coords) +t8_test_geometry_cad_tet (const int face, const int edge, double *parameters, double *test_ref_coords, + const t8_vec &test_return_coords) { #if T8_WITH_OCC /* 4 coords for face --> 3 vertices of face & element centroid - * 2 coords for edge --> 2 vertices of edge */ + * 2 coords for edge --> 2 vertices of edge + * muliplied by 3 it is equal to the dimension template parameter + */ const int num_coords = (face >= 0 ? 4 : 2); - double *out_coords = T8_ALLOC (double, num_coords * 3); + t8_vec out_coords; double tol = T8_PRECISION_EPS > 1e-10 ? T8_PRECISION_EPS : 1e-10; t8_cmesh_t cmesh = t8_create_cad_reference_tet (face, edge, parameters); @@ -475,11 +478,10 @@ t8_test_geometry_cad_tet (int face, int edge, double *parameters, double *test_r for (int i_coord = 0; i_coord < num_coords; ++i_coord) { const int offset_3d = i_coord * 3; t8_geometry_evaluate (cmesh, 0, test_ref_coords + offset_3d + (face >= 0 ? face * 12 : edge * 6), 1, - out_coords + offset_3d); - - EXPECT_VEC3_EQ (out_coords + offset_3d, test_return_coords + offset_3d, tol); + &out_coords[offset_3d]); } - T8_FREE (out_coords); + + EXPECT_VEC_EQ (out_coords, test_return_coords, tol); t8_cmesh_destroy (&cmesh); #else /* !T8_WITH_OCC */ @@ -501,11 +503,11 @@ TEST (t8_gtest_geometry_cad_tet, linked_faces) 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, // face 3 0.75, 0.25, 0.5 }; // element centroid - double surface_test_return_coords[48] - = { 0.0, 0.0, 0.0, // face vertex 0 + const t8_vec<12> surface_test_return_coords + ({ 0.0, 0.0, 0.0, // face vertex 0 1.0, 0.0, 0.0, // face vertex 1 1.0, 0.0, 1.0, // face vertex 2 - 0.7953692655, 0.25, 0.4546307344}; // element centroid (shifted) + 0.7953692655, 0.25, 0.4546307344}); // element centroid (shifted) double surface_parameters[27] = { 0, 1, 0, 0, 1, 1, // face 0 @@ -515,8 +517,8 @@ TEST (t8_gtest_geometry_cad_tet, linked_faces) /* clang-format on */ for (int i_faces = 0; i_faces < 4; i_faces++) { - t8_test_geometry_cad_tet (i_faces, -1, surface_parameters + i_faces * 6, test_ref_coords, - surface_test_return_coords); + t8_test_geometry_cad_tet<12> (i_faces, -1, surface_parameters + i_faces * 6, test_ref_coords, + surface_test_return_coords); } } @@ -530,9 +532,9 @@ TEST (t8_gtest_geometry_cad_tet, linked_edges) 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, // edge 3 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, // edge 4 1.0, 1.0, 1.0, 1.0, 0.0, 1.0 }; // edge 5 - double curve_test_return_coords[6] - = { 0.0, 0.0, 0.0, // edge vertex 0 - 1.0, 0.0, 0.0 }; // edge vertex 1 + t8_vec<6> curve_test_return_coords + ({ 0.0, 0.0, 0.0, // edge vertex 0 + 1.0, 0.0, 0.0 }); // edge vertex 1 double curve_parameters[12] = { 0, 1, // edge 0 1, 0, // edge 1 @@ -544,7 +546,8 @@ TEST (t8_gtest_geometry_cad_tet, linked_edges) /* clang-format on */ for (int i_edges = 0; i_edges < 6; ++i_edges) { - t8_test_geometry_cad_tet (-1, i_edges, curve_parameters + i_edges * 2, test_ref_coords, curve_test_return_coords); + t8_test_geometry_cad_tet<6> (-1, i_edges, curve_parameters + i_edges * 2, test_ref_coords, + curve_test_return_coords); } } #endif /* T8_WITH_OCC */ @@ -653,8 +656,8 @@ class class_2d_element_cad_curve: public testing::TestWithParam test_ref_coords_out_linear = t8_vec<9> ({ 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0 }); + const t8_vec<9> test_ref_coords_out_curved = t8_vec<9> ({ 0.0, 0.0, 0.0, 0.5, -0.2, 0.0, 1.0, 0.0, 0.0 }); }; TEST_P (class_2d_element_cad_curve, t8_check_2d_element_cad_curve) @@ -689,18 +692,22 @@ TEST_P (class_2d_element_cad_curve, t8_check_2d_element_cad_curve) /* Commit the cmesh */ t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD); - double out_coords[3]; + t8_3D_vec out_coords; /* out_coords should be equal to the input ref_coords. */ for (size_t i_coord = 0; i_coord < T8_ECLASS_MAX_DIM; ++i_coord) { t8_geometry_evaluate (cmesh, 0, (eclass == T8_ECLASS_QUAD ? test_ref_coords_quad_in : test_ref_coords_tri_in) + i_coord * T8_ECLASS_MAX_DIM + i_orientation * 9, - 1, out_coords); - - EXPECT_VEC3_EQ ( - (curvature == 0 ? test_ref_coords_out_linear : test_ref_coords_out_curved) + i_coord * T8_ECLASS_MAX_DIM, - out_coords, T8_PRECISION_EPS); + 1, out_coords.data ()); + + //t8_vec<9> checked_coords = (curvature == 0 ? test_ref_coords_out_linear : test_ref_coords_out_curved); + t8_3D_vec checked_coords; + for (int i = 0; i < 3; ++i) { + checked_coords[i] + = (curvature == 0 ? test_ref_coords_out_linear : test_ref_coords_out_curved)[i_coord * T8_ECLASS_MAX_DIM + i]; + } + EXPECT_VEC_EQ (checked_coords, out_coords, T8_PRECISION_EPS); } t8_cmesh_destroy (&cmesh); } @@ -764,8 +771,8 @@ class class_2d_element_linear_cad_surface: public testing::TestWithParam test_ref_coords = t8_vec<27> ({ 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 1.0, + 0.0, 0.0, 0.5, 0.0, 0.5, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5, 0.0 }); /* TODO: use randomised test_ref_coords, because the out_coords should be the same, no matter the test_ref_coord. */ }; @@ -795,13 +802,16 @@ TEST_P (class_2d_element_linear_cad_surface, t8_check_2d_element_linear_cad_surf /* Commit the cmesh */ t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD); - double out_coords[3]; + t8_3D_vec out_coords; /* `out_coords` should be equal to the input `ref_coords`. */ for (size_t i_coord = 0; i_coord < (eclass == T8_ECLASS_QUAD ? 9 : 6); ++i_coord) { - t8_geometry_evaluate (cmesh, 0, test_ref_coords + i_coord * 3, 1, out_coords); - - EXPECT_VEC3_EQ (test_ref_coords + i_coord * 3, out_coords, T8_PRECISION_EPS); + t8_geometry_evaluate (cmesh, 0, &test_ref_coords[i_coord * 3], 1, out_coords.data ()); + t8_3D_vec checked_coords; + for (int i = 0; i < 3; ++i) { + checked_coords[i] = test_ref_coords[i_coord * T8_ECLASS_MAX_DIM + i]; + } + EXPECT_VEC_EQ (checked_coords, out_coords, T8_PRECISION_EPS); } } @@ -874,10 +884,11 @@ class class_2d_element_curved_cad_surface: public testing::TestWithParam test_ref_coords_out = t8_vec<27> ({ 0.0, 0.0, 0.0, 0.5, -0.2, 0.0, 1.0, 0.0, 0.0, 0.5, 0.5, 0.2, 0.0, 1.0, + 0.0, -0.2, 0.5, 0.0, 0.5, 1.2, 0.0, 1.0, 1.0, 0.0, 1.2, 0.5, 0.0 }); + const t8_vec<27> test_ref_coords_in + = t8_vec<27> ({ 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 1.0, + 0.0, 0.0, 0.5, 0.0, 0.5, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5, 0.0 }); }; TEST_P (class_2d_element_curved_cad_surface, t8_check_2d_element_curved_cad_surface) @@ -904,13 +915,16 @@ TEST_P (class_2d_element_curved_cad_surface, t8_check_2d_element_curved_cad_surf /* Commit the cmesh */ t8_cmesh_commit (cmesh, sc_MPI_COMM_WORLD); - double out_coords[3]; + t8_3D_vec out_coords; /* out_coords should be equal to the input ref_coords. */ for (size_t i_coord = 0; i_coord < (eclass == T8_ECLASS_QUAD ? 9 : 6); ++i_coord) { - t8_geometry_evaluate (cmesh, 0, test_ref_coords_in + i_coord * 3, 1, out_coords); - - EXPECT_VEC3_EQ (test_ref_coords_out + i_coord * 3, out_coords, T8_PRECISION_EPS); + t8_geometry_evaluate (cmesh, 0, test_ref_coords_in.data () + i_coord * 3, 1, out_coords.data ()); + t8_3D_vec checked_coords; + for (int i = 0; i < 3; ++i) { + checked_coords[i] = test_ref_coords_out[i_coord * T8_ECLASS_MAX_DIM + i]; + } + EXPECT_VEC_EQ (checked_coords, out_coords, T8_PRECISION_EPS); } } @@ -1001,20 +1015,26 @@ t8_create_cad_reference_prism (int face, int edge, double *parameters) * \param [in] comm The mpi communicator to use. * \return Returns 1 if passed, 0 if failed. */ +template void -t8_test_geometry_cad_prism (int face, int edge, double *parameters, double *test_ref_coords, double *test_return_coords) +t8_test_geometry_cad_prism (const int face, const int edge, double *parameters, double *test_ref_coords, + const t8_vec &test_return_coords) { #if T8_WITH_OCC - double out_coords[3]; + t8_3D_vec out_coords; double tol = T8_PRECISION_EPS > 1e-10 ? T8_PRECISION_EPS : 1e-10; const int face_vertices = (face <= 2 ? 4 : 3); t8_cmesh_t cmesh = t8_create_cad_reference_prism (face, edge, parameters); for (int i_coord = 0; i_coord < (face >= 0 ? face_vertices : 3); ++i_coord) { - t8_geometry_evaluate (cmesh, 0, test_ref_coords + i_coord * 3 + (face >= 0 ? face * 12 : edge * 9), 1, out_coords); - - EXPECT_VEC3_EQ (out_coords, test_return_coords + i_coord * 3, tol); + t8_geometry_evaluate (cmesh, 0, test_ref_coords + i_coord * 3 + (face >= 0 ? face * 12 : edge * 9), 1, + out_coords.data ()); + t8_3D_vec checked_coords; + for (int i = 0; i < 3; ++i) { + checked_coords[i] = test_return_coords[i_coord * T8_ECLASS_MAX_DIM + i]; + } + EXPECT_VEC_EQ (out_coords, checked_coords, tol); } t8_cmesh_destroy (&cmesh); @@ -1035,15 +1055,15 @@ TEST (t8_gtest_geometry_cad_prism, linked_faces) 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 2.0, 2.0, 2.0 }; // face 4 /* The 2.0's at face 3 and 4 are placeholders, because both faces only have 3 vertices. */ - double surface_test_return_coords_quad[12] - = { 0.0, 0.0, 0.0, // face vertex 0 + const t8_vec<12> surface_test_return_coords_quad + ({ 0.0, 0.0, 0.0, // face vertex 0 1.0, 0.0, 0.0, // face vertex 1 0.0, 0.0, 1.0, // face vertex 2 - 1.0, 0.0, 1.0 }; // face vertex 3 - double surface_test_return_coords_tri[9] - = { 0.0, 0.0, 0.0, // face vertex 0 + 1.0, 0.0, 1.0 }); // face vertex 3 + const t8_vec<9> surface_test_return_coords_tri + ({ 0.0, 0.0, 0.0, // face vertex 0 1.0, 0.0, 0.0, // face vertex 1 - 1.0, 1.0, 0.0 }; // face vertex 2 + 1.0, 1.0, 0.0 }); // face vertex 2 double surface_parameters[40] = { 1, 1, 1, 0, 0, 1, 0, 0, // face 0 @@ -1055,8 +1075,16 @@ TEST (t8_gtest_geometry_cad_prism, linked_faces) /* clang-format on */ for (int i_faces = 0; i_faces < 5; i_faces++) { - t8_test_geometry_cad_prism (i_faces, -1, surface_parameters + i_faces * 8, test_ref_coords, - (i_faces <= 2 ? surface_test_return_coords_quad : surface_test_return_coords_tri)); + if (i_faces <= 2) { + const t8_vec<12> surface_test_return_coords = surface_test_return_coords_quad; + t8_test_geometry_cad_prism (i_faces, -1, surface_parameters + i_faces * 8, test_ref_coords, + surface_test_return_coords); + } + else { + const t8_vec<9> surface_test_return_coords = surface_test_return_coords_tri; + t8_test_geometry_cad_prism (i_faces, -1, surface_parameters + i_faces * 8, test_ref_coords, + surface_test_return_coords); + } } } @@ -1073,10 +1101,10 @@ TEST (t8_gtest_geometry_cad_prism, linked_edges) 1.0, 0.0, 1.0, 1.0, 0.0, 0.5, 1.0, 0.0, 0.0, // edge 6 1.0, 1.0, 0.0, 1.0, 1.0, 0.5, 1.0, 1.0, 1.0, // edge 7 0.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0 }; // edge 8 - double curve_test_return_coords[9] - = { 0.0, 0.0, 0.0, // edge vertex 0 + t8_vec<9> curve_test_return_coords + ({ 0.0, 0.0, 0.0, // edge vertex 0 0.4999500215, 0.0000523914, 0.4000007585, // center of edge - 1.0, 0.0, 0.0 }; // edge vertex 1 + 1.0, 0.0, 0.0 }); // edge vertex 1 double curve_parameters[18] = { 0, 1, // edge 0 1, 0, // edge 1 diff --git a/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_lagrange.cxx b/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_lagrange.cxx index 07beda39b4..20ef839986 100644 --- a/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_lagrange.cxx +++ b/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_lagrange.cxx @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_linear.cxx b/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_linear.cxx index ee453aadce..6697c5370c 100644 --- a/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_linear.cxx +++ b/test/t8_geometry/t8_geometry_implementations/t8_gtest_geometry_linear.cxx @@ -34,6 +34,7 @@ #include #include #include +#include class geometry_test: public testing::TestWithParam> { public: @@ -108,7 +109,7 @@ TEST_P (geometry_test, cmesh_geometry) /* Create random points in [0,1]^d and check if they are mapped correctly. */ - double point_mapped[3]; + t8_3D_point point_mapped; const t8_geometry_c *cmesh_geom; /* Double check that the geometry is the linear axis aligned geometry. */ @@ -119,7 +120,7 @@ TEST_P (geometry_test, cmesh_geometry) srand (seed); for (int ipoint = 0; ipoint < T8_NUM_SAMPLE_POINTS; ++ipoint) { - double point[3] = { 0 }; + t8_3D_point point ({ 0.0, 0.0, 0.0 }); /* Compute random coordinates in [0,1]. * These are seen as reference coordinates in the single * cmesh tree. Our geometry will map them into the physical @@ -131,9 +132,9 @@ TEST_P (geometry_test, cmesh_geometry) } /* Evaluate the geometry */ - t8_geometry_evaluate (cmesh, 0, point, 1, point_mapped); + t8_geometry_evaluate (cmesh, 0, point.data (), 1, point_mapped.data ()); /* Check that the first dim coordinates are the same */ - EXPECT_VEC3_EQ (point, point_mapped, T8_PRECISION_SQRT_EPS); + EXPECT_VEC_EQ (point, point_mapped, T8_PRECISION_SQRT_EPS); } } diff --git a/test/t8_geometry/t8_gtest_point_inside.cxx b/test/t8_geometry/t8_gtest_point_inside.cxx index 5d2cd6266d..0c4ec3f39c 100644 --- a/test/t8_geometry/t8_gtest_point_inside.cxx +++ b/test/t8_geometry/t8_gtest_point_inside.cxx @@ -342,7 +342,7 @@ auto print_test = [] (const testing::TestParamInfo #include -#include +#include #ifndef CUSTOM_ASSERTION_HXX #define CUSTOM_ASSERTION_HXX @@ -88,25 +88,20 @@ element_equality (const char *ts_expr, const char *tree_class_expr, const char * * \param[in] precision Test equality up to this prescision * \return testing::AssertionResult */ -testing::AssertionResult -vec3_equality (const char *vec_1_expr, const char *vec_2_expr, const char *precision_expr, const double vec_1[3], - const double vec_2[3], const double precision) +template +constexpr testing::AssertionResult +vec_equality (const char *vec_1_expr, const char *vec_2_expr, const char *precision_expr, const T &vec_1, + const T &vec_2, const double precision) { - if (t8_vec_eq (vec_1, vec_2, precision)) { + if (t8_eq (vec_1, vec_2, precision)) { return testing::AssertionSuccess (); } else { -#if T8_ENABLE_DEBUG - return testing::AssertionFailure () << vec_1[0] << " " << vec_1[1] << " " << vec_1[2] << " " << vec_1_expr - << " is not equal to \n" - << vec_2[0] << " " << vec_2[1] << " " << vec_2[2] << " " << vec_2_expr << " \n" + return testing::AssertionFailure () << vec_1_expr << " is not equal to " << vec_2_expr << " \n" << "Precision given by " << precision_expr << " " << precision; -#else - return testing::AssertionFailure () << vec_1_expr << " is not equal to " << vec_2_expr; -#endif } } -#define EXPECT_VEC3_EQ(vec_1, vec_2, precision) EXPECT_PRED_FORMAT3 (vec3_equality, (vec_1), (vec_2), (precision)) +#define EXPECT_VEC_EQ(vec_1, vec_2, precision) EXPECT_PRED_FORMAT3 (vec_equality, (vec_1), (vec_2), (precision)) #endif /* CUSTOM_ASSERTION_HXX */ diff --git a/test/t8_gtest_eclass.cxx b/test/t8_gtest_eclass.cxx index 4abe116e66..b812d102b1 100644 --- a/test/t8_gtest_eclass.cxx +++ b/test/t8_gtest_eclass.cxx @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include class gtest_eclass: public testing::TestWithParam { @@ -103,19 +103,19 @@ TEST_P (gtest_eclass, eclass_face_orientation) /* clang-format on */ double vec_0[3]; double vec_1[3]; - t8_vec_axpyz (vertices + 3 * v[1], vertices + 3 * v[0], vec_0, -1.0); - t8_vec_axpyz (vertices + 3 * v[2], vertices + 3 * v[0], vec_1, -1.0); + t8_axpyz (vertices + 3 * v[1], vertices + 3 * v[0], vec_0, -1.0); + t8_axpyz (vertices + 3 * v[2], vertices + 3 * v[0], vec_1, -1.0); double normal[3]; - t8_vec_cross (vec_0, vec_1, normal); + t8_cross_3D (vec_0, vec_1, normal); double centroid[3] = { 0.0 }; for (int ivertex = 0; ivertex < t8_eclass_num_vertices[ieclass]; ivertex++) { centroid[0] += vertices[3 * ivertex]; centroid[1] += vertices[3 * ivertex + 1]; centroid[2] += vertices[3 * ivertex + 2]; } - t8_vec_ax (centroid, 1.0 / t8_eclass_num_vertices[ieclass]); - t8_vec_axpy (vertices + 3 * v[0], centroid, -1.0); - const double c_n = t8_vec_dot (centroid, normal); + t8_ax (centroid, 1.0 / t8_eclass_num_vertices[ieclass]); + t8_axpy (vertices + 3 * v[0], centroid, -1.0); + const double c_n = t8_dot (centroid, normal); const int orientation = c_n > 0 ? 0 : 1; EXPECT_EQ (orientation, t8_eclass_face_orientation[ieclass][iface]); } diff --git a/test/t8_gtest_macros.hxx b/test/t8_gtest_macros.hxx index 9ebae45da7..27ba906424 100644 --- a/test/t8_gtest_macros.hxx +++ b/test/t8_gtest_macros.hxx @@ -43,7 +43,7 @@ auto print_eclass = [] (const testing::TestParamInfo &info) { return * Number of points to use in tests * */ -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 #define T8_NUM_SAMPLE_POINTS 1000 #else #define T8_NUM_SAMPLE_POINTS 10000 diff --git a/test/t8_gtest_main.cxx b/test/t8_gtest_main.cxx index e0008b01c0..48239d582c 100644 --- a/test/t8_gtest_main.cxx +++ b/test/t8_gtest_main.cxx @@ -1,3 +1,25 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + #include #include diff --git a/test/t8_gtest_schemes.hxx b/test/t8_gtest_schemes.hxx index e1a56b2565..45b1360b9a 100644 --- a/test/t8_gtest_schemes.hxx +++ b/test/t8_gtest_schemes.hxx @@ -33,12 +33,23 @@ create_from_scheme_id (const int scheme_id) switch (scheme_id) { case 0: return t8_scheme_new_default (); + case 1: + return t8_scheme_new_standalone (); default: SC_ABORT_NOT_REACHED (); return nullptr; } } -#define AllSchemes ::testing::Combine (::testing::Values (0), ::testing::Range (T8_ECLASS_ZERO, T8_ECLASS_COUNT)) +static const char *t8_scheme_to_string[] = { "default", "standalone" }; + +auto print_all_schemes = [] (const testing::TestParamInfo> &info) { + return std::string (t8_scheme_to_string[std::get<0> (info.param)]) + "_" + + t8_eclass_to_string[std::get<1> (info.param)]; +}; + +#define AllSchemes ::testing::Combine (::testing::Range (0, 2), ::testing::Range (T8_ECLASS_ZERO, T8_ECLASS_COUNT)) +#define AllSchemeCollections ::testing::Values (0) +#define DefaultScheme ::testing::Combine (::testing::Values (0), ::testing::Range (T8_ECLASS_ZERO, T8_ECLASS_COUNT)) #endif /* T8_GTEST_SCHEMES_HXX */ diff --git a/test/t8_gtest_vec.cxx b/test/t8_gtest_vec.cxx index 5c7d96b87e..49d4ed86f8 100644 --- a/test/t8_gtest_vec.cxx +++ b/test/t8_gtest_vec.cxx @@ -20,63 +20,61 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* In this file we collect tests for the routines in t8_vec.h */ +/* In this file we collect tests for the routines in t8_vec.hxx */ #include -#include +#include #include -/* Wrapper for 3D vector datatype */ -typedef double t8_test_vec[3]; /* Accuracy used for comparisons with correct result */ #define epsilon 1e-9 -/* test the t8_vec_norm function */ +/* test the t8_norm function */ TEST (t8_gtest_vec, norm) { - const t8_test_vec zero = { 0, 0, 0 }; - const t8_test_vec onetwothree = { 1, 2, 3 }; - const t8_test_vec arbitrary = { -.05, 3.14159, 42 }; + const t8_3D_vec zero ({ 0, 0, 0 }); + const t8_3D_vec onetwothree ({ 1, 2, 3 }); + const t8_3D_vec arbitrary ({ -.05, 3.14159, 42 }); const double normonetwothree = sqrt (1 + 4 + 9); const double normarbitrary = 42.117360883; - EXPECT_EQ (t8_vec_norm (zero), 0); - EXPECT_NEAR (t8_vec_norm (onetwothree), normonetwothree, epsilon); - EXPECT_NEAR (t8_vec_norm (arbitrary), normarbitrary, epsilon); + EXPECT_EQ (t8_norm (zero), 0); + EXPECT_NEAR (t8_norm (onetwothree), normonetwothree, epsilon); + EXPECT_NEAR (t8_norm (arbitrary), normarbitrary, epsilon); } -/* test the t8_vec_dist function */ +/* test the t8_dist function */ TEST (t8_gtest_vec, dist) { - const t8_test_vec zero = { 0, 0, 0 }; - const t8_test_vec onetwothree = { 1, 2, 3 }; - const t8_test_vec arbitrary = { -.05, 3.14159, 42 }; + const t8_3D_point zero ({ 0, 0, 0 }); + const t8_3D_point onetwothree ({ 1, 2, 3 }); + const t8_3D_point arbitrary ({ -.05, 3.14159, 42 }); const double distzeroonetwothree = sqrt (1 + 4 + 9); const double distarbitraryonetwothree = 39.030830477; - EXPECT_VEC3_EQ (zero, zero, T8_PRECISION_EPS); - EXPECT_VEC3_EQ (onetwothree, onetwothree, T8_PRECISION_EPS); - EXPECT_NEAR (t8_vec_dist (onetwothree, zero), distzeroonetwothree, epsilon); - EXPECT_NEAR (t8_vec_dist (zero, onetwothree), distzeroonetwothree, epsilon); - EXPECT_NEAR (t8_vec_dist (arbitrary, onetwothree), distarbitraryonetwothree, epsilon); - EXPECT_NEAR (t8_vec_dist (onetwothree, arbitrary), distarbitraryonetwothree, epsilon); + EXPECT_VEC_EQ (zero, zero, T8_PRECISION_EPS); + EXPECT_VEC_EQ (onetwothree, onetwothree, T8_PRECISION_EPS); + EXPECT_NEAR (t8_dist (onetwothree, zero), distzeroonetwothree, epsilon); + EXPECT_NEAR (t8_dist (zero, onetwothree), distzeroonetwothree, epsilon); + EXPECT_NEAR (t8_dist (arbitrary, onetwothree), distarbitraryonetwothree, epsilon); + EXPECT_NEAR (t8_dist (onetwothree, arbitrary), distarbitraryonetwothree, epsilon); } -/* test the t8_vec_ax function */ +/* test the t8_ax function */ TEST (t8_gtest_vec, ax) { - const t8_test_vec czero = { 0, 0, 0 }; - const t8_test_vec conetwothree = { 1, 2, 3 }; - const t8_test_vec carbitrary = { -.05, 3.14159, 42 }; - t8_test_vec zero = { 0, 0, 0 }; - t8_test_vec onetwothree = { 1, 2, 3 }; - t8_test_vec arbitrary = { -.05, 3.14159, 42 }; + const t8_3D_vec czero ({ 0, 0, 0 }); + const t8_3D_vec conetwothree ({ 1, 2, 3 }); + const t8_3D_vec carbitrary ({ -.05, 3.14159, 42 }); + t8_3D_vec zero ({ 0, 0, 0 }); + t8_3D_vec onetwothree ({ 1, 2, 3 }); + t8_3D_vec arbitrary ({ -.05, 3.14159, 42 }); const double alpha = 5.1234; /* Compute Y = alpha * Y */ - t8_vec_ax (zero, alpha); - t8_vec_ax (onetwothree, alpha); - t8_vec_ax (arbitrary, alpha); + t8_ax (zero, alpha); + t8_ax (onetwothree, alpha); + t8_ax (arbitrary, alpha); /* Check results */ for (int i = 0; i < 3; ++i) { @@ -86,21 +84,21 @@ TEST (t8_gtest_vec, ax) } } -/* test the t8_vec_axy function */ +/* test the t8_axy function */ TEST (t8_gtest_vec, axy) { - const t8_test_vec czero = { 0, 0, 0 }; - const t8_test_vec conetwothree = { 1, 2, 3 }; - const t8_test_vec carbitrary = { -.05, 3.14159, 42 }; - t8_test_vec zero; - t8_test_vec onetwothree; - t8_test_vec arbitrary; + const t8_3D_vec czero ({ 0, 0, 0 }); + const t8_3D_vec conetwothree ({ 1, 2, 3 }); + const t8_3D_vec carbitrary ({ -.05, 3.14159, 42 }); + t8_3D_vec zero; + t8_3D_vec onetwothree; + t8_3D_vec arbitrary; const double alpha = 5.1234; /* Compute Y = alpha * X */ - t8_vec_axy (czero, zero, alpha); - t8_vec_axy (conetwothree, onetwothree, alpha); - t8_vec_axy (carbitrary, arbitrary, alpha); + t8_axy (czero, zero, alpha); + t8_axy (conetwothree, onetwothree, alpha); + t8_axy (carbitrary, arbitrary, alpha); /* Check results */ for (int i = 0; i < 3; ++i) { @@ -110,22 +108,22 @@ TEST (t8_gtest_vec, axy) } } -/* test the t8_vec_axb function */ +/* test the t8_axb function */ TEST (t8_gtest_vec, axb) { - const t8_test_vec czero = { 0, 0, 0 }; - const t8_test_vec conetwothree = { 1, 2, 3 }; - const t8_test_vec carbitrary = { -.05, 3.14159, 42 }; + const t8_3D_vec czero ({ 0, 0, 0 }); + const t8_3D_vec conetwothree ({ 1, 2, 3 }); + const t8_3D_vec carbitrary ({ -.05, 3.14159, 42 }); const double b = 2.71828; - t8_test_vec zero; - t8_test_vec onetwothree; - t8_test_vec arbitrary; + t8_3D_vec zero; + t8_3D_vec onetwothree; + t8_3D_vec arbitrary; const double alpha = 5.1234; /* Compute Y = alpha * Y + b */ - t8_vec_axb (czero, zero, alpha, b); - t8_vec_axb (conetwothree, onetwothree, alpha, b); - t8_vec_axb (carbitrary, arbitrary, alpha, b); + t8_axb (czero, zero, alpha, b); + t8_axb (conetwothree, onetwothree, alpha, b); + t8_axb (carbitrary, arbitrary, alpha, b); /* Check results */ for (int i = 0; i < 3; ++i) { @@ -135,23 +133,23 @@ TEST (t8_gtest_vec, axb) } } -/* test the t8_vec_axpy function */ +/* test the t8_axpy function */ TEST (t8_gtest_vec, axpy) { - const t8_test_vec czero = { 0, 0, 0 }; - const t8_test_vec conetwothree = { 1, 2, 3 }; - const t8_test_vec carbitrary = { -.05, 3.14159, 42 }; - const t8_test_vec init = { 3, 2.71828, -4.1 }; + const t8_3D_vec czero ({ 0, 0, 0 }); + const t8_3D_vec conetwothree ({ 1, 2, 3 }); + const t8_3D_vec carbitrary ({ -.05, 3.14159, 42 }); + const t8_3D_vec init ({ 3, 2.71828, -4.1 }); /* The next three vecs must be initialized with the values of init */ - t8_test_vec zero = { 3, 2.71828, -4.1 }; - t8_test_vec onetwothree = { 3, 2.71828, -4.1 }; - t8_test_vec arbitrary { 3, 2.71828, -4.1 }; + t8_3D_vec zero ({ 3, 2.71828, -4.1 }); + t8_3D_vec onetwothree ({ 3, 2.71828, -4.1 }); + t8_3D_vec arbitrary ({ 3, 2.71828, -4.1 }); const double alpha = 5.1234; /* Compute Y = Y + alpha * Y */ - t8_vec_axpy (czero, zero, alpha); - t8_vec_axpy (conetwothree, onetwothree, alpha); - t8_vec_axpy (carbitrary, arbitrary, alpha); + t8_axpy (czero, zero, alpha); + t8_axpy (conetwothree, onetwothree, alpha); + t8_axpy (carbitrary, arbitrary, alpha); /* Check results */ for (int i = 0; i < 3; ++i) { @@ -161,46 +159,46 @@ TEST (t8_gtest_vec, axpy) } } -/* test the t8_vec_axpyz function */ +/* test the t8_axpyz function */ TEST (t8_gtest_vec, axpyz) { - const t8_test_vec czero = { 0, 0, 0 }; - const t8_test_vec conetwothree = { 1, 2, 3 }; - const t8_test_vec carbitrary = { -.05, 3.14159, 42 }; - const t8_test_vec init = { 3, 2.71828, -4.1 }; - t8_test_vec Z; + const t8_3D_vec czero ({ 0, 0, 0 }); + const t8_3D_vec conetwothree ({ 1, 2, 3 }); + const t8_3D_vec carbitrary ({ -.05, 3.14159, 42 }); + const t8_3D_vec init ({ 3, 2.71828, -4.1 }); + t8_3D_vec Z; const double alpha = 5.1234; /* Z = init + alpha * zero */ - t8_vec_axpyz (czero, init, Z, alpha); + t8_axpyz (czero, init, Z, alpha); for (int i = 0; i < 3; ++i) { EXPECT_NEAR (Z[i], init[i] + alpha * czero[i], epsilon); } /* Z = init + alpha * conetwothree */ - t8_vec_axpyz (conetwothree, init, Z, alpha); + t8_axpyz (conetwothree, init, Z, alpha); for (int i = 0; i < 3; ++i) { EXPECT_NEAR (Z[i], init[i] + alpha * conetwothree[i], epsilon); } /* Z = init + alpha * carbitrary */ - t8_vec_axpyz (carbitrary, init, Z, alpha); + t8_axpyz (carbitrary, init, Z, alpha); for (int i = 0; i < 3; ++i) { EXPECT_NEAR (Z[i], init[i] + alpha * carbitrary[i], epsilon); } } -/* test the t8_vec_dot function */ +/* test the t8_dot function */ TEST (t8_gtest_vec, dot) { - const t8_test_vec czero = { 0, 0, 0 }; - const t8_test_vec conetwothree = { 1, 2, 3 }; - const t8_test_vec carbitrary = { -.05, 3.14159, 42 }; + const t8_3D_vec czero ({ 0, 0, 0 }); + const t8_3D_vec conetwothree ({ 1, 2, 3 }); + const t8_3D_vec carbitrary ({ -.05, 3.14159, 42 }); double result; /* Dot product with 0 is 0 */ for (int i = 0; i < 3; ++i) { - EXPECT_EQ (t8_vec_dot (czero, czero), 0); - EXPECT_EQ (t8_vec_dot (czero, conetwothree), 0); - EXPECT_EQ (t8_vec_dot (czero, carbitrary), 0); + EXPECT_EQ (t8_dot (czero, czero), 0); + EXPECT_EQ (t8_dot (czero, conetwothree), 0); + EXPECT_EQ (t8_dot (czero, carbitrary), 0); } /* Test dot product of the remaining two vecs */ @@ -208,52 +206,90 @@ TEST (t8_gtest_vec, dot) for (int i = 0; i < 3; ++i) { result += conetwothree[i] * carbitrary[i]; } - EXPECT_NEAR (t8_vec_dot (conetwothree, carbitrary), result, epsilon); + EXPECT_NEAR (t8_dot (conetwothree, carbitrary), result, epsilon); /* For the dot-product of a vector with itself we use the square of its norm */ - result = t8_vec_norm (conetwothree) * t8_vec_norm (conetwothree); - EXPECT_NEAR (t8_vec_dot (conetwothree, conetwothree), result, epsilon); + result = t8_norm (conetwothree) * t8_norm (conetwothree); + EXPECT_NEAR (t8_dot (conetwothree, conetwothree), result, epsilon); - result = t8_vec_norm (carbitrary) * t8_vec_norm (carbitrary); - EXPECT_NEAR (t8_vec_dot (carbitrary, carbitrary), result, epsilon); + result = t8_norm (carbitrary) * t8_norm (carbitrary); + EXPECT_NEAR (t8_dot (carbitrary, carbitrary), result, epsilon); } -/* test the t8_vec_cross function */ -TEST (t8_gtest_vec, cross) +/* test the t8_cross_3D function */ +TEST (t8_gtest_vec, cross_3D) { - const t8_test_vec czero = { 0, 0, 0 }; - const t8_test_vec e1 = { 1, 0, 0 }; - const t8_test_vec e2 = { 0, 1, 0 }; - const t8_test_vec e3 = { 0, 0, 1 }; - t8_test_vec cross; + const t8_3D_vec czero ({ 0, 0, 0 }); + const t8_3D_vec e1 ({ 1, 0, 0 }); + const t8_3D_vec e2 ({ 0, 1, 0 }); + const t8_3D_vec e3 ({ 0, 0, 1 }); + t8_3D_vec cross; /* cross product with 0 is 0 */ - t8_vec_cross (czero, czero, cross); + t8_cross_3D (czero, czero, cross); for (int i = 0; i < 3; ++i) { EXPECT_EQ (cross[i], 0); } - t8_vec_cross (e1, czero, cross); + t8_cross_3D (e1, czero, cross); for (int i = 0; i < 3; ++i) { EXPECT_EQ (cross[i], 0); } - t8_vec_cross (e2, czero, cross); + t8_cross_3D (e2, czero, cross); for (int i = 0; i < 3; ++i) { EXPECT_EQ (cross[i], 0); } /* e1 x e2 = e3 */ - t8_vec_cross (e1, e2, cross); - EXPECT_VEC3_EQ (cross, e3, T8_PRECISION_EPS); + t8_cross_3D (e1, e2, cross); + EXPECT_VEC_EQ (cross, e3, T8_PRECISION_EPS); /* e2 x e3 = e1 */ - t8_vec_cross (e2, e3, cross); - EXPECT_VEC3_EQ (cross, e1, T8_PRECISION_EPS); + t8_cross_3D (e2, e3, cross); + EXPECT_VEC_EQ (cross, e1, T8_PRECISION_EPS); +} + +TEST (t8_gtest_vec, cross_2D) +{ + const t8_vec<2> zero ({ 0, 0 }); + const t8_vec<2> e1 ({ 1, 0 }); // Unit vector along x-axis + const t8_vec<2> e2 ({ 0, 1 }); // Unit vector along y-axis + const t8_vec<2> v1 ({ 3, 4 }); // Arbitrary vector + const t8_vec<2> v2 ({ -4, 3 }); // Perpendicular to v1 + + double cross; + + // Cross product with zero vector is 0 + cross = t8_cross_2D (zero, zero); + EXPECT_EQ (cross, 0.0); + + cross = t8_cross_2D (e1, zero); + EXPECT_EQ (cross, 0.0); + + cross = t8_cross_2D (e2, zero); + EXPECT_EQ (cross, 0.0); + + // Cross product of e1 and e2 is +1 (counterclockwise) + cross = t8_cross_2D (e1, e2); + EXPECT_EQ (cross, 1.0); + + // Cross product of e2 and e1 is -1 (clockwise) + cross = t8_cross_2D (e2, e1); + EXPECT_EQ (cross, -1.0); + + // Cross product of v1 and v2 + cross = t8_cross_2D (v1, v2); + EXPECT_EQ (cross, 3 * 3 - 4 * -4); // 9 + 16 = 25 + EXPECT_EQ (cross, 25.0); + + // Cross product of v2 and v1 (reverse order) + cross = t8_cross_2D (v2, v1); + EXPECT_EQ (cross, -25.0); } TEST (t8_gtest_vec, check_less_or_equal) { - const t8_test_vec one = { 1.0, 1.0, 1.0 }; - const t8_test_vec one_minus_eps = { 1.0 - T8_PRECISION_EPS, 1.0 - T8_PRECISION_EPS, 1.0 - T8_PRECISION_EPS }; + const t8_3D_vec one ({ 1.0, 1.0, 1.0 }); + const t8_3D_vec one_minus_eps ({ 1.0 - T8_PRECISION_EPS, 1.0 - T8_PRECISION_EPS, 1.0 - T8_PRECISION_EPS }); - EXPECT_VEC3_EQ (one, one_minus_eps, T8_PRECISION_EPS); + EXPECT_VEC_EQ (one, one_minus_eps, T8_PRECISION_EPS); } diff --git a/test/t8_schemes/t8_gtest_ancestor_id.cxx b/test/t8_schemes/t8_gtest_ancestor_id.cxx index d88b641335..3f68ee4a5e 100644 --- a/test/t8_schemes/t8_gtest_ancestor_id.cxx +++ b/test/t8_schemes/t8_gtest_ancestor_id.cxx @@ -75,7 +75,7 @@ class class_ancestor_id: public TestDFS { TEST_P (class_ancestor_id, t8_recursive_dfs_ancestor_id) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 4; #else const int maxlvl = 6; @@ -83,4 +83,4 @@ TEST_P (class_ancestor_id, t8_recursive_dfs_ancestor_id) check_recursive_dfs_to_max_lvl (maxlvl); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_ancestor_id, class_ancestor_id, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_ancestor_id, class_ancestor_id, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_bfs_base.hxx b/test/t8_schemes/t8_gtest_bfs_base.hxx new file mode 100644 index 0000000000..2fc1ef3cd8 --- /dev/null +++ b/test/t8_schemes/t8_gtest_bfs_base.hxx @@ -0,0 +1,111 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** + * \file t8_gtest_bfs_base.hxx + * A base class for breadth first search tests. + */ + +#ifndef T8_GTEST_BFS_BASE_HXX +#define T8_GTEST_BFS_BASE_HXX + +#include +#include +#include +#include + +class TestBFS: public testing::TestWithParam> { + public: + /** recursive tests check something for all descendants of a starting element (currently only root) upto maxlevel +*/ + virtual void + check_element () {}; + + /** recursive breadth first search to iterate over all descendants of elem up to max_bfs_recursion_level */ + void + check_recursive_bfs_to_max_lvl (const int max_bfs_recursion_level) + { + std::queue queue; + queue.push (element); + + while (!queue.empty () && current_level <= max_bfs_recursion_level) { + const int level_size = queue.size (); + for (int ilevel = 0; ilevel < level_size; ++ilevel) { + element = queue.front (); + queue.pop (); + + // Process the current element + check_element (); + + // Stop at maximum recursion level. + if (current_level < max_bfs_recursion_level) { + // Add all children of the current element to the queue + const int num_children = scheme->element_get_num_children (eclass, element); + for (int jchild = 0; jchild < num_children; ++jchild) { + t8_element_t *child; + scheme->element_new (eclass, 1, &child); + scheme->element_get_child (eclass, element, jchild, child); + queue.push (child); + } + } + // Free the current element + scheme->element_destroy (eclass, 1, &element); + } + ++current_level; + } + } + + void + bfs_test_setup () + { + const int scheme_id = std::get<0> (GetParam ()); + scheme = create_from_scheme_id (scheme_id); + eclass = std::get<1> (GetParam ()); + scheme->element_new (eclass, 1, &element); + scheme->get_root (eclass, element); + } + + void + bfs_test_teardown () + { + scheme->unref (); + } + + void + SetUp () override + { + bfs_test_setup (); + } + + void + TearDown () override + { + bfs_test_teardown (); + } + + const t8_scheme *scheme; + t8_eclass_t eclass; + t8_element_t *element; + int current_level = 0; +}; + +#endif /* T8_GTEST_BFS_BASE_HXX */ diff --git a/test/t8_schemes/t8_gtest_boundary_extrude.cxx b/test/t8_schemes/t8_gtest_boundary_extrude.cxx index db07ff89b1..c717790413 100644 --- a/test/t8_schemes/t8_gtest_boundary_extrude.cxx +++ b/test/t8_schemes/t8_gtest_boundary_extrude.cxx @@ -84,7 +84,7 @@ class class_test_boundary_extrude: public TestDFS { TEST_P (class_test_boundary_extrude, test_boundary_extrude_dfs) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 4; #else const int maxlvl = 6; @@ -92,4 +92,4 @@ TEST_P (class_test_boundary_extrude, test_boundary_extrude_dfs) check_recursive_dfs_to_max_lvl (maxlvl); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_boundary_extrude, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_boundary_extrude, DefaultScheme); diff --git a/test/t8_schemes/t8_gtest_child_parent_face.cxx b/test/t8_schemes/t8_gtest_child_parent_face.cxx index fa2e0d9280..2652a96601 100644 --- a/test/t8_schemes/t8_gtest_child_parent_face.cxx +++ b/test/t8_schemes/t8_gtest_child_parent_face.cxx @@ -72,7 +72,7 @@ class class_child_parent_face: public TestDFS { TEST_P (class_child_parent_face, t8_recursive_dfs_child_parent_face) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 4; #else const int maxlvl = 6; @@ -80,4 +80,4 @@ TEST_P (class_child_parent_face, t8_recursive_dfs_child_parent_face) check_recursive_dfs_to_max_lvl (maxlvl); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_child_parent_face, class_child_parent_face, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_child_parent_face, class_child_parent_face, DefaultScheme); diff --git a/test/t8_schemes/t8_gtest_descendant.cxx b/test/t8_schemes/t8_gtest_descendant.cxx index ade669a018..8df2d2dc5e 100644 --- a/test/t8_schemes/t8_gtest_descendant.cxx +++ b/test/t8_schemes/t8_gtest_descendant.cxx @@ -159,4 +159,4 @@ TEST_P (class_schemes_descendant, test_recursive_descendant) t8_large_step_descendant (elem, desc, test, scheme, eclass, maxlvl); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_descendant, class_schemes_descendant, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_descendant, class_schemes_descendant, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_element_count_leaves.cxx b/test/t8_schemes/t8_gtest_element_count_leaves.cxx index 0c7820df9d..9e74fea9bb 100644 --- a/test/t8_schemes/t8_gtest_element_count_leaves.cxx +++ b/test/t8_schemes/t8_gtest_element_count_leaves.cxx @@ -128,4 +128,4 @@ TEST_P (class_element_leaves, test_element_count_leaves_one_level) scheme->element_destroy (eclass, 1, &element); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_element_count_leaves, class_element_leaves, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_element_count_leaves, class_element_leaves, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_element_ref_coords.cxx b/test/t8_schemes/t8_gtest_element_ref_coords.cxx index f6f75c3a31..f1b2a61aa9 100644 --- a/test/t8_schemes/t8_gtest_element_ref_coords.cxx +++ b/test/t8_schemes/t8_gtest_element_ref_coords.cxx @@ -27,13 +27,13 @@ #include #include -#include +#include #include #include #include #include -#if T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 #define MAX_LEVEL_REF_COORD_TEST 3 #else #define MAX_LEVEL_REF_COORD_TEST 4 @@ -90,10 +90,10 @@ t8_element_centroid_by_vertex_coords (const t8_forest_t forest, const t8_locidx_ /* Evaluate the geometry */ t8_geometry_evaluate (cmesh, gtreeid, vertex_ref_coords, 1, vertex_out_coords); /* coordinates = coordinates + vertex_coords */ - t8_vec_axpy (vertex_out_coords, coordinates, 1); + t8_axpy (vertex_out_coords, coordinates, 1); } /* Divide each coordinate by num_vertices */ - t8_vec_ax (coordinates, 1. / num_vertices); + t8_ax (coordinates, 1. / num_vertices); } /** diff --git a/test/t8_schemes/t8_gtest_equal.cxx b/test/t8_schemes/t8_gtest_equal.cxx index c38516d990..7631429055 100644 --- a/test/t8_schemes/t8_gtest_equal.cxx +++ b/test/t8_schemes/t8_gtest_equal.cxx @@ -76,7 +76,7 @@ class class_test_equal: public TestDFS { TEST_P (class_test_equal, test_equal_dfs) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 3; #else const int maxlvl = 5; @@ -84,4 +84,4 @@ TEST_P (class_test_equal, test_equal_dfs) check_recursive_dfs_to_max_lvl (maxlvl); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_equal, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_equal, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_face_corner.cxx b/test/t8_schemes/t8_gtest_face_corner.cxx new file mode 100644 index 0000000000..211dfa5342 --- /dev/null +++ b/test/t8_schemes/t8_gtest_face_corner.cxx @@ -0,0 +1,113 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include "t8_gtest_dfs_base.hxx" + +/** This test is used to test the corner_face and face_corner scheme functions. + * The first test gets the corners of all faces of an element and checks if the reverse function can get the correct face of the corner. + * The second test does the same but in reverse order. + */ +class class_face_corner_test: public TestDFS { + + void + check_element () override + { + const int num_faces = scheme->element_get_num_faces (eclass, element); + + const int max_num_faces = scheme->element_get_max_num_faces (eclass, element); + EXPECT_LE (num_faces, max_num_faces); + + for (int iface = 0; iface < num_faces; iface++) { + const t8_element_shape_t face_shape = scheme->element_get_face_shape (eclass, element, iface); + const int num_vertices = t8_element_shape_num_vertices (face_shape); + for (int ivertex = 0; ivertex < num_vertices; ivertex++) { + const int corner = scheme->element_get_face_corner (eclass, element, iface, ivertex); + /* Only valid for hypercubes*/ + const int num_corner_faces = t8_eclass_to_dimension[eclass]; + + bool found_face = false; + for (int icorner_face = 0; icorner_face < num_corner_faces; icorner_face++) { + const int face = scheme->element_get_corner_face (eclass, element, corner, icorner_face); + if (face == iface) { + found_face = true; + } + } + EXPECT_TRUE (found_face); + } + } + + const int num_corners = scheme->element_get_num_corners (eclass, element); + + for (int icorner = 0; icorner < num_corners; icorner++) { + /* Only valid for hypercubes*/ + const int num_faces = t8_eclass_to_dimension[eclass]; + for (int iface = 0; iface < num_faces; iface++) { + const int face = scheme->element_get_corner_face (eclass, element, icorner, iface); + + const t8_element_shape_t face_shape = scheme->element_get_face_shape (eclass, element, face); + const int num_face_corners = t8_element_shape_num_vertices (face_shape); + + bool found_corner = false; + for (int iface_corner = 0; iface_corner < num_face_corners; iface_corner++) { + const int corner = scheme->element_get_face_corner (eclass, element, face, iface_corner); + if (corner == icorner) { + found_corner = true; + } + } + EXPECT_TRUE (found_corner); + } + } + } + + protected: + void + SetUp () override + { + dfs_test_setup (); + } + void + TearDown () override + { + /* Destroy DFS test */ + dfs_test_teardown (); + } +}; + +TEST_P (class_face_corner_test, test_equal_dfs) +{ +#ifdef T8_ENABLE_LESS_TESTS + const int maxlvl = 3; +#else + const int maxlvl = 5; +#endif + check_recursive_dfs_to_max_lvl (maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_face_corner_test, + testing::Combine (AllSchemeCollections, + ::testing::Range (T8_ECLASS_VERTEX, (t8_eclass_t) (T8_ECLASS_HEX + 1))), + print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_face_descendant.cxx b/test/t8_schemes/t8_gtest_face_descendant.cxx index b83c1f4892..e4cd31495b 100644 --- a/test/t8_schemes/t8_gtest_face_descendant.cxx +++ b/test/t8_schemes/t8_gtest_face_descendant.cxx @@ -111,7 +111,7 @@ class class_descendant: public TestDFS { TEST_P (class_descendant, t8_check_face_desc) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 3; #else const int maxlvl = 5; @@ -120,4 +120,4 @@ TEST_P (class_descendant, t8_check_face_desc) check_recursive_dfs_to_max_lvl (maxlvl); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_element_face_descendant, class_descendant, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_element_face_descendant, class_descendant, DefaultScheme); diff --git a/test/t8_schemes/t8_gtest_face_neigh.cxx b/test/t8_schemes/t8_gtest_face_neigh.cxx index 006f9b96e8..7ce88ee876 100644 --- a/test/t8_schemes/t8_gtest_face_neigh.cxx +++ b/test/t8_schemes/t8_gtest_face_neigh.cxx @@ -56,7 +56,7 @@ class face_neigh: public testing::TestWithParam> { const t8_scheme *scheme; t8_eclass_t eclass; -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 3; #else const int maxlvl = 4; @@ -199,4 +199,4 @@ TEST_P (face_neigh, recursive_check_diff) t8_recursive_check_diff (child, element, neigh, scheme, eclass, maxlvl, level); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_face_neigh, face_neigh, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_face_neigh, face_neigh, DefaultScheme); diff --git a/test/t8_schemes/t8_gtest_find_parent.cxx b/test/t8_schemes/t8_gtest_find_parent.cxx index 1e6dcf8519..26ccf94055 100644 --- a/test/t8_schemes/t8_gtest_find_parent.cxx +++ b/test/t8_schemes/t8_gtest_find_parent.cxx @@ -66,7 +66,7 @@ class class_find_parent: public TestDFS { TEST_P (class_find_parent, t8_compute_child_find_parent) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 4; #else const int maxlvl = 6; @@ -74,4 +74,4 @@ TEST_P (class_find_parent, t8_compute_child_find_parent) check_recursive_dfs_to_max_lvl (maxlvl); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_find_parent, class_find_parent, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_find_parent, class_find_parent, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_init_linear_id.cxx b/test/t8_schemes/t8_gtest_get_linear_id.cxx similarity index 94% rename from test/t8_schemes/t8_gtest_init_linear_id.cxx rename to test/t8_schemes/t8_gtest_get_linear_id.cxx index 1403046fb5..97905f878c 100644 --- a/test/t8_schemes/t8_gtest_init_linear_id.cxx +++ b/test/t8_schemes/t8_gtest_get_linear_id.cxx @@ -28,7 +28,7 @@ #include #include -class linear_id: public testing::TestWithParam> { +class get_linear_id: public testing::TestWithParam> { protected: void SetUp () override @@ -68,11 +68,11 @@ t8_test_init_linear_id_refine_everything (t8_forest_t forest, t8_forest_t forest } /* Iterate over the leaves of a uniformly refined forest and check the id*/ -TEST_P (linear_id, uniform_forest) +TEST_P (get_linear_id, uniform_forest) { t8_forest_t forest, forest_adapt; t8_cmesh_t cmesh; -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 5; #else const int maxlvl = 6; @@ -115,9 +115,9 @@ TEST_P (linear_id, uniform_forest) /* Test, if the linear_id of descendants of an element is the same as the id of element * (on the level defined by the element) */ -TEST_P (linear_id, id_at_other_level) +TEST_P (get_linear_id, id_at_other_level) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int max_lvl = 3; /* Maximal level to compute elements on */ const int add_lvl = 3; /* maxlvl + add_lvl is the level of the descendants*/ #else @@ -148,4 +148,4 @@ TEST_P (linear_id, id_at_other_level) } } -INSTANTIATE_TEST_SUITE_P (t8_test_init_linear_id, linear_id, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_test_get_linear_id, get_linear_id, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_input_equal_output.cxx b/test/t8_schemes/t8_gtest_input_equal_output.cxx new file mode 100644 index 0000000000..cc83139f61 --- /dev/null +++ b/test/t8_schemes/t8_gtest_input_equal_output.cxx @@ -0,0 +1,99 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include "t8_gtest_dfs_base.hxx" +#include + +/* The mainly tested function in this test is element_get_children_at_face. The function allows that the input elem is equal children[0] (output). +Therefore the test checks if this is true and the input is not overridden in the loop or is overridden in the last iteration. +This is tested by comparing the output of two cases. In the first case the condition is not true in the second case elem is equal children[0]. */ +class class_test_equal: public TestDFS { + void + check_element () override + { + for (int iface = 0; iface < scheme->element_get_num_faces (eclass, element); iface++) { + + const int num_children = scheme->element_get_num_face_children (eclass, element, iface); + + elems1 = T8_ALLOC (t8_element_t *, num_children); + elems2 = T8_ALLOC (t8_element_t *, num_children); + + scheme->element_new (eclass, num_children, elems1); + scheme->element_new (eclass, num_children, elems2); + + /* Copy element */ + t8_element_t *first_of_elems1 = elems1[0]; + scheme->element_copy (eclass, element, first_of_elems1); + + scheme->element_get_children_at_face (eclass, element, iface, elems2, num_children, NULL); + + scheme->element_get_children_at_face (eclass, first_of_elems1, iface, elems1, num_children, NULL); + + for (int ichild = 0; ichild < num_children; ichild++) { + t8_element_t *child1 = elems1[ichild]; + t8_element_t *child2 = elems2[ichild]; + + EXPECT_ELEM_EQ (scheme, eclass, child1, child2); + } + + /* Destroy element */ + scheme->element_destroy (eclass, num_children, elems1); + scheme->element_destroy (eclass, num_children, elems2); + + T8_FREE (elems1); + T8_FREE (elems2); + } + } + + protected: + void + SetUp () override + { + dfs_test_setup (); + /* Get element and initialize it */ + } + void + TearDown () override + { + /* Destroy DFS test */ + dfs_test_teardown (); + } + t8_element_t **elems1; + t8_element_t **elems2; +}; + +TEST_P (class_test_equal, test_equal_dfs) +{ +#if T8CODE_TEST_LEVEL == 1 + const int maxlvl = 3; +#else + const int maxlvl = 5; +#endif + check_recursive_dfs_to_max_lvl (maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_equal, DefaultScheme, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_nca.cxx b/test/t8_schemes/t8_gtest_nca.cxx index fa94665fa3..50d5a61abe 100644 --- a/test/t8_schemes/t8_gtest_nca.cxx +++ b/test/t8_schemes/t8_gtest_nca.cxx @@ -151,7 +151,7 @@ TEST_P (nca, nca_check_deep) * \param[in] parent_a An initialized element, descendant of \a correct_nca, not a descendant or ancestor of \a parent_b. \a desc_a will be a child of it * \param[in] parent_b An initialized element, descendant of \a correct_nca, not a descendant or ancestor of \a parent_a. \a desc_b will be a child of it * \param[in] max_lvl The maximal depth of the recursion - * \param[in] scheme the scheme to use + * \param[in] scheme The scheme to use */ static void t8_recursive_nca_check (t8_element_t *check_nca, t8_element_t *desc_a, t8_element_t *desc_b, t8_element_t *check, @@ -224,7 +224,7 @@ t8_recursive_nca_check (t8_element_t *check_nca, t8_element_t *desc_a, t8_elemen * output of element_get_nca.*/ TEST_P (nca, recursive_check) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int recursion_depth = 3; #else /* User lower recursion depth for pyramids, it takes to much time otherwise */ @@ -284,7 +284,7 @@ TEST_P (nca, recursive_check_higher_level) /* Initialization for recursive_nca_check */ num_children = scheme->element_get_num_children (tree_class, correct_nca_high_level); if (num_children > 1) { - /* Compute children on to different branches in the tree an test them. + /* Compute children on two different branches in the tree an test them. * This ensures, that the nca of all their descendants has to be correct_nca_high_level*/ for (k = 0; k < num_children; k++) { scheme->element_get_child (tree_class, correct_nca_high_level, k, parent_a); @@ -315,4 +315,4 @@ TEST_P (nca, recursive_check_higher_level) scheme->element_destroy (tree_class, 1, &correct_nca_high_level); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_nca, nca, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_nca, nca, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_pack_unpack.cxx b/test/t8_schemes/t8_gtest_pack_unpack.cxx index 22e68b08fd..865f8980c5 100644 --- a/test/t8_schemes/t8_gtest_pack_unpack.cxx +++ b/test/t8_schemes/t8_gtest_pack_unpack.cxx @@ -127,7 +127,7 @@ class class_test_pack: public TestDFS { TEST_P (class_test_pack, test_equal_dfs) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 4; #else const int maxlvl = 6; @@ -135,4 +135,4 @@ TEST_P (class_test_pack, test_equal_dfs) check_recursive_dfs_to_max_lvl (maxlvl); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_pack, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_pack, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_root.cxx b/test/t8_schemes/t8_gtest_root.cxx index e7c820e77a..3aee580ce7 100644 --- a/test/t8_schemes/t8_gtest_root.cxx +++ b/test/t8_schemes/t8_gtest_root.cxx @@ -70,4 +70,4 @@ TEST_P (root, equals_linear_id_0_0) scheme->element_destroy (eclass, 1, &root_compare); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_root, root, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_root, root, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_set_linear_id.cxx b/test/t8_schemes/t8_gtest_set_linear_id.cxx new file mode 100644 index 0000000000..81f7ae449e --- /dev/null +++ b/test/t8_schemes/t8_gtest_set_linear_id.cxx @@ -0,0 +1,80 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include "t8_gtest_bfs_base.hxx" + +/** In this test we iterate through all elements. + * On every level we check if the element is equal to the element we get when setting it from the linear id. + * The id_counter is then increased to match the id of the next leaf. After we have reached the last element on a level, + * we increase the level and reset the id_counter to 0. + */ +class class_test_set_linear_id: public TestBFS { + void + check_element () override + { + if (computed_level < current_level) { + computed_level = current_level; + id_counter = 0; + } + scheme->element_set_linear_id (eclass, test_element, current_level, id_counter); + id_counter++; + EXPECT_ELEM_EQ (scheme, eclass, element, test_element); + } + + protected: + void + SetUp () override + { + bfs_test_setup (); + /* Get element and initialize it */ + scheme->element_new (eclass, 1, &test_element); + } + void + TearDown () override + { + /* Destroy element */ + scheme->element_destroy (eclass, 1, &test_element); + + /* Destroy BFS test */ + bfs_test_teardown (); + } +#if T8CODE_TEST_LEVEL == 1 + const int maxlvl = 3; +#else + const int maxlvl = 5; +#endif + int computed_level = 0; + t8_linearidx_t id_counter = 0; + t8_element_t *test_element; +}; + +TEST_P (class_test_set_linear_id, test_linear_id_bfs) +{ + check_recursive_bfs_to_max_lvl (maxlvl); +} + +INSTANTIATE_TEST_SUITE_P (t8_gtest_test_all_imps, class_test_set_linear_id, AllSchemes, print_all_schemes); diff --git a/test/t8_schemes/t8_gtest_successor.cxx b/test/t8_schemes/t8_gtest_successor.cxx index b1c4c2aa77..9728b4e79d 100644 --- a/test/t8_schemes/t8_gtest_successor.cxx +++ b/test/t8_schemes/t8_gtest_successor.cxx @@ -136,7 +136,7 @@ t8_deep_successor (t8_element_t *element, t8_element_t *successor, t8_element_t TEST_P (class_successor, test_recursive_and_deep_successor) { -#ifdef T8_ENABLE_LESS_TESTS +#if T8CODE_TEST_LEVEL == 1 const int maxlvl = 3; #else const int maxlvl = 4; @@ -154,4 +154,4 @@ TEST_P (class_successor, test_recursive_and_deep_successor) t8_deep_successor (element, successor, last, scheme, tree_class); } -INSTANTIATE_TEST_SUITE_P (t8_gtest_successor, class_successor, AllSchemes); +INSTANTIATE_TEST_SUITE_P (t8_gtest_successor, class_successor, AllSchemes, print_all_schemes); diff --git a/test/t8_types/t8_gtest_random_accessible.cxx b/test/t8_types/t8_gtest_random_accessible.cxx new file mode 100644 index 0000000000..8adfc84c18 --- /dev/null +++ b/test/t8_types/t8_gtest_random_accessible.cxx @@ -0,0 +1,72 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* In this file we collect tests for the routines in t8_vec.hxx */ + +#include +#include +#include + +/* test the t8_types operator bracket */ +TEST (RandomAccessibleTest, OperatorBracket) +{ + t8_vec<4> test; + test = { 10, 20, 30, 40 }; + + EXPECT_EQ (test[0], 10); + EXPECT_EQ (test[1], 20); + EXPECT_EQ (test[2], 30); + EXPECT_EQ (test[3], 40); + + *test.begin () = 77; + EXPECT_EQ (test[0], 77); +} + +TEST (RandomAccessibleTest, ConstOperatorBracket) +{ + const t8_3D_vec test { { 10, 20, 30 } }; + EXPECT_EQ (test[0], 10); + EXPECT_EQ (test[1], 20); + EXPECT_EQ (test[2], 30); +} + +TEST (RandomAccessibleTest, DataMethod) +{ + t8_3D_vec test; + test = { 42, 100, 200 }; + + EXPECT_EQ (test.data (), &test[0]); + EXPECT_EQ (*test.data (), 42); +} + +TEST (RandomAccessibleTest, EmptyContainer) +{ + t8_vec<0> test; + EXPECT_EQ (test.begin (), test.end ()); +} + +TEST (RandomAccessibleTest, OutOfBoundsAccess) +{ + t8_vec<3> test; + test = { 10, 20, 30 }; + EXPECT_NO_THROW (test[2]); +} diff --git a/test/t8_types/t8_gtest_type.cxx b/test/t8_types/t8_gtest_type.cxx new file mode 100644 index 0000000000..2a23e9e0ec --- /dev/null +++ b/test/t8_types/t8_gtest_type.cxx @@ -0,0 +1,196 @@ +/* +This file is part of t8code. +t8code is a C library to manage a collection (a forest) of multiple +connected adaptive space-trees of general element classes in parallel. + +Copyright (C) 2024 the developers + +t8code 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. + +t8code 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 t8code; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* Tags to differencce between strong types */ +struct dummy_int +{ +}; +struct dummy_int_2 +{ +}; +struct dummy_double +{ +}; + +struct dummy_ref_int +{ +}; + +struct dummy_ref_double +{ +}; + +struct dummy_double_3 +{ +}; + +struct dummy_name_tag +{ +}; + +typedef struct +{ + int x; + double y; +} int_and_double_struct; + +struct int_and_double_tag +{ +}; + +/* Strong types for testing */ +using DummyInt = T8Type; +using DummyInt2 = T8Type; +using DummyDouble = T8Type; +using DummyRefInt = T8Type; +using DummyRefDouble = T8Type; +using Dummy3DVec = T8Type, dummy_double_3, EqualityComparable, Swapable>; +using DummyName = T8Type; +using int_and_double = T8Type; + +/** + * Test if the the strong types are different. + * + */ +TEST (t8_gtest_type, strong_type_equality) +{ + EXPECT_TRUE ((std::is_same::value)); + EXPECT_TRUE ((std::is_same::value)); + EXPECT_TRUE ((std::is_same::value)); + + EXPECT_FALSE ((std::is_same::value)); + EXPECT_FALSE ((std::is_same::value)); + EXPECT_FALSE ((std::is_same::value)); + EXPECT_FALSE ((std::is_same::value)); + EXPECT_FALSE ((std::is_same::value)); + EXPECT_FALSE ((std::is_same::value)); +} + +/** + * Check the sizes of the strong types. + */ +TEST (t8_gtest_type, strong_type_size) +{ + EXPECT_EQ (sizeof (DummyInt), sizeof (int)); + EXPECT_EQ (sizeof (DummyDouble), sizeof (double)); + EXPECT_EQ (sizeof (DummyRefInt), sizeof (int *)); + EXPECT_EQ (sizeof (DummyRefDouble), sizeof (double *)); + EXPECT_EQ (sizeof (Dummy3DVec), sizeof (std::array)); + EXPECT_EQ (sizeof (DummyName), sizeof (std::string)); + EXPECT_EQ (sizeof (int_and_double), sizeof (int_and_double_struct)); +} + +/** + * Test if each strong type holds the correct values (e.g. the get operator works). + * + */ +TEST (t8_gtest_type, strong_type_get) +{ + DummyInt dummy_int (5); + std::cout << "dummy_int: " << dummy_int << "\n"; + DummyInt2 dummy_int_2 (10); + DummyDouble dummy_double (3.14); + DummyRefInt dummy_ref_int (dummy_int.get ()); + DummyRefDouble dummy_ref_double (dummy_double.get ()); + + EXPECT_EQ (dummy_int.get (), 5); + EXPECT_EQ (dummy_int_2.get (), 10); + EXPECT_EQ (dummy_double.get (), 3.14); + EXPECT_EQ (dummy_ref_int.get (), 5); + EXPECT_EQ (dummy_ref_double.get (), 3.14); + + std::vector vec (10000); + + std::iota (vec.begin (), vec.end (), DummyInt (0)); + std::for_each (vec.begin (), vec.end (), [&dummy_int] (DummyInt &i) { i += dummy_int; }); + + std::for_each (vec.begin (), vec.end (), [n = 5] (const DummyInt &i) mutable { EXPECT_EQ (i.get (), n++); }); +} + +/** + * Test if the operators of the strong types work. + */ +TEST (t8_gtest_type, operators) +{ + DummyInt my_int (5); + DummyInt my_other_int (10); + DummyInt my_result_int = my_int + my_other_int; + EXPECT_EQ (my_result_int.get (), 15); + my_result_int = my_int - my_other_int; + EXPECT_EQ (my_result_int.get (), -5); + my_result_int = my_int * my_other_int; + EXPECT_EQ (my_result_int.get (), 50); + my_result_int = my_int / my_other_int; + EXPECT_EQ (my_result_int.get (), 0); + my_result_int = ++my_int; + EXPECT_EQ (my_result_int.get (), 6); + my_result_int = --my_int; + EXPECT_EQ (my_result_int.get (), 5); + + Dummy3DVec vec1 ({ 1.0, 2.0, 3.0 }); + Dummy3DVec vec2 = vec1; + EXPECT_EQ (vec1, vec2); + Dummy3DVec vec3 ({ 2.0, 2.0, 3.0 }); + EXPECT_NE (vec1, vec3); + swap (vec1, vec3); + EXPECT_NE (vec1, vec3); + EXPECT_EQ (vec3, vec2); +} + +TEST (t8_gtest_type, use_constexpr) +{ + constexpr DummyInt my_int (5); + constexpr DummyInt my_other_int (10); + constexpr DummyInt my_result_int = my_int + my_other_int; + static_assert (my_result_int.get () == 15, "constexpr operator+ failed"); + constexpr DummyInt my_result_int2 = my_int - my_other_int; + static_assert (my_result_int2.get () == -5, "constexpr operator- failed"); + constexpr DummyInt my_result_int3 = my_int * my_other_int; + static_assert (my_result_int3.get () == 50, "constexpr operator* failed"); + constexpr DummyInt my_result_int4 = my_int / my_other_int; + static_assert (my_result_int4.get () == 0, "constexpr operator/ failed"); + constexpr DummyInt my_int_eq (5); + static_assert (my_int_eq == my_int, "constexpr operator== failed"); + static_assert (my_int_eq != my_other_int, "constexpr operator!= failed"); +} + +/** + * Test if the strong types are hashable. + */ +TEST (t8_gtest_type, hashable) +{ + std::unordered_map my_map + = { { DummyName ("one"), 1 }, { DummyName ("two"), 2 }, { DummyName ("three"), 3 } }; + + EXPECT_EQ (my_map[DummyName ("one")], 1); + EXPECT_EQ (my_map[DummyName ("two")], 2); + EXPECT_EQ (my_map[DummyName ("three")], 3); +} diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 8e749f3854..088d4c0dec 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -25,7 +25,7 @@ function( add_t8_tutorial ) set_target_properties( ${ADD_T8_TUTORIAL_NAME} PROPERTIES EXPORT_COMPILE_COMMANDS ON ) endif( T8CODE_EXPORT_COMPILE_COMMANDS ) - install( TARGETS ${ADD_T8_TUTORIAL_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin ) + install( TARGETS ${ADD_T8_TUTORIAL_NAME} DESTINATION bin ) endfunction() #copy tutorial files to the exact same location in the builddir as they are in the source dir @@ -44,7 +44,7 @@ add_t8_tutorial( NAME t8_step7_interpolation SOURCES general/t8_step7 add_t8_tutorial( NAME t8_tutorial_build_cmesh SOURCES general/t8_tutorial_build_cmesh.cxx general/t8_tutorial_build_cmesh_main.cxx) add_t8_tutorial( NAME t8_tutorial_search SOURCES general/t8_tutorial_search.cxx general/t8_step3_adapt_forest.cxx ) add_t8_tutorial( NAME t8_features_curved_meshes SOURCES features/t8_features_curved_meshes.cxx) - +add_t8_tutorial( NAME t8_tutorial_build_scheme SOURCES general/t8_tutorial_build_scheme.cxx) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_hex.geo) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_quad.geo) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_tet.geo) diff --git a/tutorials/general/t8_step1_coarsemesh.cxx b/tutorials/general/t8_step1_coarsemesh.cxx index e4cd562ba5..5e7a92dcd6 100644 --- a/tutorials/general/t8_step1_coarsemesh.cxx +++ b/tutorials/general/t8_step1_coarsemesh.cxx @@ -40,7 +40,7 @@ #include /* cmesh-writer interface. */ #include /* A collection of exemplary cmeshes */ -/* Builds cmesh of 6 tetrahedra that build up a unit cube. +/** Builds cmesh of 6 tetrahedra that build up a unit cube. * \param [in] comm MPI Communicator to use. * \return The coarse mesh. */ @@ -65,7 +65,7 @@ t8_step1_build_tetcube_coarse_mesh (sc_MPI_Comm comm) return cmesh; } -/* Write vtk (or more accurately vtu) files of the cmesh. +/** Write vtk (or more accurately vtu) files of the cmesh. * \param [in] cmesh A coarse mesh. * \param [in] prefix A string that is used as a prefix of the output files. * @@ -79,7 +79,7 @@ t8_step1_write_cmesh_vtk (t8_cmesh_t cmesh, const char *prefix) t8_cmesh_vtk_write_file (cmesh, prefix); } -/* Destroy a cmesh. This will free all allocated memory. +/** Destroy a cmesh. This will free all allocated memory. * \param [in] cmesh A cmesh. */ static void diff --git a/tutorials/general/t8_step2_uniform_forest.cxx b/tutorials/general/t8_step2_uniform_forest.cxx index 4931c807fc..a9480860e8 100644 --- a/tutorials/general/t8_step2_uniform_forest.cxx +++ b/tutorials/general/t8_step2_uniform_forest.cxx @@ -52,7 +52,7 @@ #include /* default refinement scheme. */ #include -/* Builds cmesh of 2 prisms that build up a unit cube. +/** Builds cmesh of 2 prisms that build up a unit cube. * See step1 for a detailed description. * \param [in] comm MPI Communicator to use. * \return The coarse mesh. @@ -69,7 +69,7 @@ t8_step2_build_prismcube_coarse_mesh (sc_MPI_Comm comm) return cmesh; } -/* Build a uniform forest on a cmesh +/** Build a uniform forest on a cmesh * using the default refinement scheme. * \param [in] comm MPI Communicator to use. * \param [in] cmesh The coarse mesh to use. @@ -83,13 +83,13 @@ t8_step2_build_uniform_forest (sc_MPI_Comm comm, t8_cmesh_t cmesh, int level) t8_forest_t forest; const t8_scheme *scheme = t8_scheme_new_default (); - /* Creat the uniform forest. */ + /* Create the uniform forest. */ forest = t8_forest_new_uniform (cmesh, scheme, level, 0, comm); return forest; } -/* Write vtk (or more accurately vtu) files of the forest. +/** Write vtk (or more accurately vtu) files of the forest. * \param [in] forest A forest. * \param [in] prefix A string that is used as a prefix of the output files. * @@ -102,7 +102,7 @@ t8_step2_write_forest_vtk (t8_forest_t forest, const char *prefix) t8_forest_write_vtk (forest, prefix); } -/* Destroy a forest. This will free all allocated memory. +/** Destroy a forest. This will free all allocated memory. * \param [in] forest A forest. * NOTE: This will also free the memory of the scheme and the cmesh, since * the forest took ownership of them. diff --git a/tutorials/general/t8_step3_adapt_forest.cxx b/tutorials/general/t8_step3_adapt_forest.cxx index 7007b7c229..2dd2d98c0f 100644 --- a/tutorials/general/t8_step3_adapt_forest.cxx +++ b/tutorials/general/t8_step3_adapt_forest.cxx @@ -33,7 +33,7 @@ * of any element will change by at most +-1. * * How you can experiment here: - * - Look at the paraview output files of the unifomr and the adapted forest. + * - Look at the paraview output files of the uniform and the adapted forest. * For the adapted forest you can apply a slice filter to look into the cube. * - Run the program with different process numbers. You should see that refining is * independent of the number of processes, but coarsening is not. @@ -45,7 +45,7 @@ * - Use t8_productionf to print the local number of elements on each process. * Notice, that the uniform forest is evenly distributed, but that the adapted forest * is not. This is due to the fact that we do not repartition our forest here. - * - Add a maximum refinement level to the adapt_data struct and use non-recursive refinement. + * - Add a maximum refinement level to the adapt_data struct and use recursive refinement. * Do not refine an element if it has reached the maximum level. (Hint: scheme->element_get_level) */ @@ -56,14 +56,11 @@ #include /* save forest */ #include /* geometrical information of the forest */ #include /* default refinement scheme. */ -#include /* Basic operations on 3D vectors. */ +#include /* Basic operations on 3D vectors. */ #include T8_EXTERN_C_BEGIN (); -/* This is our own defined data that we will pass on to the - * adaptation callback. */ - /** The adaptation callback function. This function will be called once for each element * and the return value decides whether this element should be refined or not. * return > 0 -> This element should get refined. @@ -80,8 +77,8 @@ T8_EXTERN_C_BEGIN (); * \param [in] which_tree The process local id of the current tree. * \param [in] tree_class The eclass of \a which_tree. * \param [in] lelement_id The tree local index of the current element (or the first of the family). - * \param [in] scheme The refinement scheme for this tree's element class. - * \param [in] is_family if 1, the first \a num_elements entries in \a elements form a family. If 0, they do not. + * \param [in] scheme The refinement scheme for this tree's element class. + * \param [in] is_family If 1, the first \a num_elements entries in \a elements form a family. If 0, they do not. * \param [in] num_elements The number of entries in \a elements elements that are defined. * \param [in] elements The element or family of elements to consider for refinement/coarsening. */ @@ -110,7 +107,7 @@ t8_step3_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_ t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid); /* Compute the distance to our sphere midpoint. */ - dist = t8_vec_dist (centroid, adapt_data->midpoint); + dist = t8_dist (centroid, adapt_data->midpoint); if (dist < adapt_data->refine_if_inside_radius) { /* Refine this element. */ return 1; diff --git a/tutorials/general/t8_step4_partition_balance_ghost.cxx b/tutorials/general/t8_step4_partition_balance_ghost.cxx index 80d08ac7f4..fffead74ba 100644 --- a/tutorials/general/t8_step4_partition_balance_ghost.cxx +++ b/tutorials/general/t8_step4_partition_balance_ghost.cxx @@ -25,7 +25,7 @@ * This is step4 of the t8code tutorials. * After generating a coarse mesh (step1), building a uniform forest * on it (step2) and adapting this forest (step3) - * we will now lear how to control the forest creation in more detail, + * we will now learn how to control the forest creation in more detail, * how to partition and balance a forest and how to generate a layer of ghost elements. * * Partition: Each forest is distributed among the MPI processes. Partitioning a forest means @@ -269,6 +269,8 @@ t8_step4_main (int argc, char **argv) t8_step3_print_forest_information (forest); /* Write forest to vtu files. */ t8_forest_write_vtk_ext (forest, prefix_partition_ghost, 1, 1, 1, 1, 1, 0, 1, 0, NULL); + t8_global_productionf (" [step4] Wrote repartitioned forest with ghost layer to vtu files: %s*\n", + prefix_partition_ghost); /* * Balance diff --git a/tutorials/general/t8_step5_element_data.cxx b/tutorials/general/t8_step5_element_data.cxx index b3ec59e0f1..c5ebe289fe 100644 --- a/tutorials/general/t8_step5_element_data.cxx +++ b/tutorials/general/t8_step5_element_data.cxx @@ -214,8 +214,8 @@ t8_step5_output_data_to_vtu (t8_forest_t forest, struct t8_step5_data_per_elemen element_volumes[ielem] = data[ielem].volume; } { - /* To write user defined data, we need to extended output function t8_forest_vtk_write_file - * from t8_forest_vtk.h. Despite writin user data, it also offers more control over which + /* To write user defined data, we need the extended output function t8_forest_vtk_write_file + * from t8_forest_vtk.h. Despite writing user data, it also offers more control over which * properties of the forest to write. */ int write_treeid = 1; int write_mpirank = 1; diff --git a/tutorials/general/t8_step6_stencil.cxx b/tutorials/general/t8_step6_stencil.cxx index 29c99c4795..9c5f2466c2 100644 --- a/tutorials/general/t8_step6_stencil.cxx +++ b/tutorials/general/t8_step6_stencil.cxx @@ -40,7 +40,7 @@ #include #include /* General t8code header, always include this. */ -#include /* Basic operations on 3D vectors. */ +#include /* Basic operations on 3D vectors. */ #include /* cmesh definition and basic interface. */ #include /* forest definition and basic interface. */ #include /* save forest */ @@ -107,7 +107,7 @@ t8_step6_build_forest (sc_MPI_Comm comm, int dim, int level) } /* Allocate and fill the element data array with `heights` from an arbitrary - * mathematical function. Returns a pointer to the array which is then ownded + * mathematical function. Returns a pointer to the array which is then owned * by the calling scope. */ static struct data_per_element * t8_step6_create_element_data (t8_forest_t forest) @@ -420,7 +420,7 @@ t8_step6_main (int argc, char **argv) /* Output the data to vtu files. */ t8_step6_output_data_to_vtu (forest, data, prefix_forest_with_data); - t8_global_productionf (" Wrote forest and data to %s*.\n", prefix_forest_with_data); + t8_global_productionf (" [step6] Wrote forest and data to %s*.\n", prefix_forest_with_data); /* * Clean-up @@ -431,7 +431,7 @@ t8_step6_main (int argc, char **argv) /* Destroy the forest. */ t8_forest_unref (&forest); - t8_global_productionf (" Destroyed forest.\n"); + t8_global_productionf (" [step6] Destroyed forest.\n"); sc_finalize (); diff --git a/tutorials/general/t8_step7_interpolation.cxx b/tutorials/general/t8_step7_interpolation.cxx index 6f4d86daca..14ab402dc0 100644 --- a/tutorials/general/t8_step7_interpolation.cxx +++ b/tutorials/general/t8_step7_interpolation.cxx @@ -33,13 +33,10 @@ #include #include #include /* geometrical information of the forest */ -#include +#include #include #include #include - -#include - #include T8_EXTERN_C_BEGIN (); @@ -55,15 +52,15 @@ struct t8_step7_element_data_t /* This is our own defined data that we will pass on to the * adaptation callback. */ -typedef struct t8_step7_adapt_data +struct t8_step7_adapt_data { - double midpoint[3]; /* The midpoint of our sphere. */ + t8_3D_point midpoint; /* The midpoint of our sphere. */ double refine_if_inside_radius; /* if an element's center is smaller than this value, we refine the element. */ double coarsen_if_outside_radius; /* if an element's center is larger this value, we coarsen its family. */ sc_array_t *element_data; -} t8_step7_adapt_data; +}; -/* Set the value of an element to a given entry */ +/* Set the value of an element to a given entry. */ static void t8_element_set_value (const t8_step7_adapt_data *adapt_data, t8_locidx_t ielement, double value) { @@ -72,25 +69,23 @@ t8_element_set_value (const t8_step7_adapt_data *adapt_data, t8_locidx_t ielemen *((t8_step7_element_data_t *) t8_sc_array_index_locidx (adapt_data->element_data, ielement)) = elem_data; } -/* Set the value of an element to a given entry */ +/* Set the value of an element to a given element data. */ static void t8_element_set_element (const t8_step7_adapt_data *adapt_data, t8_locidx_t ielement, t8_step7_element_data_t element) { *((t8_step7_element_data_t *) t8_sc_array_index_locidx (adapt_data->element_data, ielement)) = element; } -/* Get the value of an element to a given entry - */ +/* Get the value of an element. */ static t8_step7_element_data_t t8_element_get_value (const t8_step7_adapt_data *adapt_data, t8_locidx_t ielement) { return *((t8_step7_element_data_t *) t8_sc_array_index_locidx ((adapt_data->element_data), ielement)); } -/* This is our own defined data that we will pass on to the - * adaptation callback. */ - -/** The adaptation callback function. This function will be called once for each element +/** The adaptation callback function. + * The function is the same as the one in step 3, but with the type t8_step7_adapt_data instead of t8_step3_adapt_data. + * This function will be called once for each element * and the return value decides whether this element should be refined or not. * return > 0 -> This element should get refined. * return = 0 -> This element should not get refined. @@ -107,8 +102,8 @@ t8_element_get_value (const t8_step7_adapt_data *adapt_data, t8_locidx_t ielemen * \param [in] tree_class The eclass of \a which_tree * \param [in] tree_class The tree_class of the elements of the tree. * \param [in] lelement_id The tree local index of the current element (or the first of the family). - * \param [in] scheme The refinement scheme of the forest. - * \param [in] is_family if 1, the first \a num_elements entries in \a elements form a family. If 0, they do not. + * \param [in] scheme The refinement scheme of the forest. + * \param [in] is_family If 1, the first \a num_elements entries in \a elements form a family. If 0, they do not. * \param [in] num_elements The number of entries in \a elements elements that are defined. * \param [in] elements The element or family of elements to consider for refinement/coarsening. */ @@ -119,7 +114,7 @@ t8_step7_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_ { /* Our adaptation criterion is to look at the midpoint coordinates of the current element and if * they are inside a sphere around a given midpoint we refine, if they are outside, we coarsen. */ - double centroid[3]; /* Will hold the element midpoint. */ + t8_3D_point centroid; /* Will hold the element midpoint. */ /* In t8_step3_adapt_forest we pass a t8_step3_adapt_data pointer as user data to the * t8_forest_new_adapt function. This pointer is stored as the used data of the new forest * and we can now access it with t8_forest_get_user_data (forest). */ @@ -134,10 +129,10 @@ t8_step7_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_ T8_ASSERT (adapt_data != NULL); /* Compute the element's centroid coordinates. */ - t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid); + t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid.data ()); /* Compute the distance to our sphere midpoint. */ - dist = t8_vec_dist (centroid, adapt_data->midpoint); + dist = t8_dist (centroid, adapt_data->midpoint); if (dist < adapt_data->refine_if_inside_radius) { /* Refine this element. */ return 1; @@ -151,12 +146,12 @@ t8_step7_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_ return 0; } -/* Adapt a forest according to a callback function. +/** Adapt a forest according to a callback function. * This will create a new forest and return it. * Create a new forest that is adapted from \a forest with our adaptation callback. * We provide the adapt_data as user data that is stored as the used_data pointer of the * new forest (see also t8_forest_set_user_data). - * The 0, 0 arguments are flags that control + * * \param [in] forest_from Forest that should be adapted * \param [in] adapt_fn Function that defines how to adapt the forest - Callback function * \param [in] do_partition If non-zero the new_forest should partition the existing forest. As the second parameter @@ -187,15 +182,15 @@ t8_adapt_forest (t8_forest_t forest_from, t8_forest_adapt_t adapt_fn, int do_par return forest_new; } -/* Replace callback to decide how to interpolate a refined or coarsened element. +/** Replace callback to decide how to interpolate a refined or coarsened element. * If an element is refined, each child gets the value of its parent. * If elements are coarsened, the parent gets the average value of the children. - * Outgoing are the old elements and incoming the new ones + * Outgoing are the old elements and incoming the new ones. * \param [in] forest_old non adapted forest - * \param [in] forest_new adapted forest + * \param [in, out] forest_new adapted forest with interpolated user data for element(s) * \param [in] which_tree tree_id of the analyzed element * \param [in] tree_class The eclass of the tree - * \param [in] scheme Scheme of the forest + * \param [in] scheme Scheme of the forest * \param [in] refine ==0 - do nothing, == -1 - coarsen, == 1 - refine * \param [in] num_outgoing number of the elements not refined forest * \param [in] first_outgoing index of the old element @@ -237,7 +232,7 @@ t8_forest_replace (t8_forest_t forest_old, t8_forest_t forest_new, t8_locidx_t w t8_forest_set_user_data (forest_new, adapt_data_new); } -/* Write the forest and the data corresponding to the forest in a vtu file. +/** Write the forest and the data corresponding to the forest in a vtu file. * * \param [in] forest Forest that should written in the vtu file * \param [in] data Data corresponding to the forest @@ -288,8 +283,8 @@ t8_interpolation () t8_forest_t forest_adapt; t8_step7_element_data_t *elem_data; t8_step7_adapt_data *data; - double centroid[3]; - const double midpoint[3] = { 0.5, 0.5, 1 }; + t8_3D_point centroid; + const t8_3D_point midpoint ({ 0.5, 0.5, 1 }); const t8_scheme *scheme = t8_scheme_new_default (); /* Construct a cmesh */ @@ -318,10 +313,10 @@ t8_interpolation () const t8_element_t *element = t8_forest_get_element_in_tree (forest, itree, ielem_tree); /* Get the centroid of the local element. */ - t8_forest_element_centroid (forest, itree, element, centroid); + t8_forest_element_centroid (forest, itree, element, centroid.data ()); /* Calculation of the distance to the centroid for the referenced element */ - elem_data->values = t8_vec_dist (centroid, midpoint); + elem_data->values = t8_dist (centroid, midpoint); t8_element_set_element (data, ielem, *elem_data); } @@ -338,7 +333,9 @@ t8_interpolation () t8_forest_set_user_data (forest, data); /* Write vtu file */ - t8_write_vtu (forest, data, "t8_step7_uniform_forest"); + const char *prefix_uniform_forest = "t8_step7_uniform_forest"; + t8_write_vtu (forest, data, prefix_uniform_forest); + t8_global_productionf (" [step7] Wrote uniform forest with data to %s*.\n", prefix_uniform_forest); /* Build a second forest to store the adapted forest - keep the old one */ t8_forest_ref (forest); @@ -353,18 +350,21 @@ t8_interpolation () = sc_array_new_count (sizeof (t8_step7_element_data_t), t8_forest_get_local_num_elements (forest_adapt)); /* Initialize user_data element for the adapted forest */ - (*adapt_data).midpoint[0] = (*data).midpoint[0]; - (*adapt_data).midpoint[1] = (*data).midpoint[1]; - (*adapt_data).midpoint[2] = (*data).midpoint[2]; - (*adapt_data).refine_if_inside_radius = (*data).refine_if_inside_radius; - (*adapt_data).coarsen_if_outside_radius = (*data).coarsen_if_outside_radius; + adapt_data->midpoint[0] = data->midpoint[0]; + adapt_data->midpoint[1] = data->midpoint[1]; + adapt_data->midpoint[2] = data->midpoint[2]; + adapt_data->refine_if_inside_radius = data->refine_if_inside_radius; + adapt_data->coarsen_if_outside_radius = data->coarsen_if_outside_radius; t8_forest_set_user_data (forest_adapt, adapt_data); t8_forest_iterate_replace (forest_adapt, forest, t8_forest_replace); /* Write the adapted forest to a vtu file */ adapt_data = (struct t8_step7_adapt_data *) t8_forest_get_user_data (forest_adapt); - t8_write_vtu (forest_adapt, adapt_data, "t8_step7_adapt_forest"); + + const char *prefix_adapted_forest = "t8_step7_adapt_forest"; + t8_write_vtu (forest_adapt, adapt_data, prefix_adapted_forest); + t8_global_productionf (" [step7] Wrote adapted forest with data to %s*.\n", prefix_adapted_forest); /* Free the memory */ sc_array_destroy (adapt_data->element_data); @@ -397,7 +397,7 @@ t8_step7_main (int argc, char **argv) /* Initialize t8code with log level SC_LP_PRODUCTION. See sc.h for more info on the log levels. */ t8_init (SC_LP_PRODUCTION); - /* Create forest, define data on forest, adapt forest, interpolate data */ + /* Create forest, define data on forest, adapt forest, interpolate data. */ t8_interpolation (); sc_finalize (); diff --git a/tutorials/general/t8_tutorial_build_cmesh.cxx b/tutorials/general/t8_tutorial_build_cmesh.cxx index 122636fc49..b39afed317 100644 --- a/tutorials/general/t8_tutorial_build_cmesh.cxx +++ b/tutorials/general/t8_tutorial_build_cmesh.cxx @@ -144,13 +144,14 @@ t8_cmesh_new_periodic_hybrid_2d (sc_MPI_Comm comm) /* 1. Defining an array with all vertices */ /* Just all vertices of all trees. partly duplicated */ - double vertices[60] = { 0, 0, 0, /* tree 0, triangle */ - 0.5, 0, 0, 0.5, 0.5, 0, 0, 0, 0, /* tree 1, triangle */ - 0.5, 0.5, 0, 0, 0.5, 0, 0.5, 0, 0, /* tree 2, quad */ - 1, 0, 0, 0.5, 0.5, 0, 1, 0.5, 0, 0, 0.5, 0, /* tree 3, quad */ - 0.5, 0.5, 0, 0, 1, 0, 0.5, 1, 0, 0.5, 0.5, 0, /* tree 4, triangle */ - 1, 0.5, 0, 1, 1, 0, 0.5, 0.5, 0, /* tree 5, triangle */ - 1, 1, 0, 0.5, 1, 0 }; + double vertices[60] = { + 0, 0, 0, 0.5, 0, 0, 0.5, 0.5, 0, /* tree 0, triangle */ + 0, 0, 0, 0.5, 0.5, 0, 0, 0.5, 0, /* tree 1, triangle */ + 0.5, 0, 0, 1, 0, 0, 0.5, 0.5, 0, 1, 0.5, 0, /* tree 2, quad */ + 0, 0.5, 0, 0.5, 0.5, 0, 0, 1, 0, 0.5, 1, 0, /* tree 3, quad */ + 0.5, 0.5, 0, 1, 0.5, 0, 1, 1, 0, /* tree 4, triangle */ + 0.5, 0.5, 0, 1, 1, 0, 0.5, 1, 0 /* tree 5, triangle */ + }; /* 2. Initialization of the mesh */ t8_cmesh_t cmesh; @@ -303,7 +304,7 @@ t8_cmesh_new_hybrid_gate_3d (sc_MPI_Comm comm) t8_cmesh_register_geometry (cmesh); /* Use linear geometry */ - /* Defitition of the classes of the different trees */ + /* Definition of the classes of the different trees */ t8_cmesh_set_tree_class (cmesh, 0, T8_ECLASS_TET); t8_cmesh_set_tree_class (cmesh, 1, T8_ECLASS_TET); t8_cmesh_set_tree_class (cmesh, 2, T8_ECLASS_PRISM); @@ -492,9 +493,9 @@ t8_tutorial_build_cmesh_main (int argc, char **argv) /* Output the meshes to vtu files. */ t8_cmesh_vtk_write_file (cmesh_2D, prefix_2D); - t8_global_productionf ("[tutorial] Wrote the 2D cmesh to vtu files.\n"); + t8_global_productionf ("[tutorial] Wrote the 2D cmesh to %s*.\n", prefix_2D); t8_cmesh_vtk_write_file (cmesh_3D, prefix_3D); - t8_global_productionf ("[tutorial] Wrote the 3D cmesh to vtu files.\n"); + t8_global_productionf ("[tutorial] Wrote the 3D cmesh to %s*.\n", prefix_3D); /* * Clean-up diff --git a/tutorials/general/t8_tutorial_build_scheme.cxx b/tutorials/general/t8_tutorial_build_scheme.cxx new file mode 100644 index 0000000000..1ca261017d --- /dev/null +++ b/tutorials/general/t8_tutorial_build_scheme.cxx @@ -0,0 +1,188 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2025 the developers + + t8code 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. + + t8code 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 t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* + * In this tutorial we discuss how to use schemes in t8code. + * Schemes are used to define the refinement and coarsening behavior of the forest. Upon building the forest from a cmesh, + * the user can define a scheme that will be used for all tree-specific operations in the forest. That way the scheme + * influences how the forest is manipulated in your pipeline. + * + * In this example we will use the default scheme that is provided by t8code. This scheme uses the morton-type + * space-filling curves to order the elements in the forest. + * + * Furthermore we will also show how to mix the default scheme with a custom scheme. For that we will use the + * default scheme and the standalone scheme that is provided by t8code. If you have a custom scheme, you can follow + * the steps in this example to use it with your code. + * + * The currently provided schemes are equivalent. Therefore we expect the output to be equivalent. + * In the future, when the standalone scheme is extended, the output will differ for some element classes. + */ + +#include /* General t8code header, always include this. */ +#include /* cmesh definition and basic interface. */ +#include /* A collection of exemplary cmeshes */ +#include /* default refinement scheme. */ +#include /* standalone refinement scheme. */ +#include /* VTK writer for t8code. */ + +#include /* Scheme builder for custom schemes. */ + +/** + * A function to manually build the default scheme. +*/ +const t8_scheme * +t8_scheme_default_build_manually (void) +{ + /* A scheme builder creates a scheme. The schemes should be in this order to + * match the expected order of eclasses. The schemes are ordered from 1D to 3D */ + t8_scheme_builder builder; + + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + return builder.build_scheme (); +} + +/** + * A function to mix the standalone scheme with the default scheme. +*/ +const t8_scheme * +t8_scheme_default_build_mixed (void) +{ + t8_scheme_builder builder; + + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + /* For Quads we use the standalone scheme. */ + builder.add_eclass_scheme> (); + builder.add_eclass_scheme (); + /* For Hexahedra we use the standalone scheme. */ + builder.add_eclass_scheme> (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + builder.add_eclass_scheme (); + return builder.build_scheme (); +} + +/* The main function of this example. */ +int +main (int argc, char **argv) +{ + /* Initialize MPI. This has to happen before we initialize sc or t8code */ + int mpiret = sc_MPI_Init (&argc, &argv); + /* Error check the MPI return value. */ + SC_CHECK_MPI (mpiret); + + /* Initialize the sc library, has to happen before we initialize t8code. */ + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); + /* Initialize t8code with log level SC_LP_PRODUCTION. See sc.h for more info on the log levels. */ + t8_init (SC_LP_PRODUCTION); + + /* We will use MPI_COMM_WORLD as a communicator. */ + sc_MPI_Comm comm = sc_MPI_COMM_WORLD; + + /* Print a message on the root process. */ + t8_global_productionf (" [scheme] \n"); + t8_global_productionf (" [scheme] Hello, this is the scheme example of t8code.\n"); + t8_global_productionf (" [scheme] We will use the default scheme, a hand-built replica of the default scheme and the " + "standalone scheme.\n"); + t8_global_productionf (" [scheme] \n"); + + /* Configure the vtk_writer. */ + const bool write_treeid = true; + const bool write_mpirank = true; + const bool write_level = true; + const bool write_element_id = true; + const bool write_ghosts = true; + const bool curved_flag = false; + const std::string default_forest = "forest_with_default_scheme"; + + /* Create the vtk writer. */ + vtk_writer vtk_writer (write_treeid, write_mpirank, write_level, write_element_id, write_ghosts, + curved_flag, std::string ("test_vtk"), 0, NULL, comm); + + /* + * Build forest with default scheme. + */ + t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (comm, 0, 0); + /* t8_scheme_new_default creates the default scheme. */ + t8_forest_t forest_default = t8_forest_new_uniform (cmesh, t8_scheme_new_default (), 3, 0, comm); + + bool vtk_written = false; + /* Update the output-name */ + vtk_writer.set_fileprefix (std::string ("forest_with_default_scheme")); + /* write the forest into a vtk file. If t8code has been configured with VTK we can use the vtk-library. + * Otherwise vtk-compatible ASCII-output is created. */ +#if T8_WITH_VTK + vtk_written = vtk_writer.write_with_API (forest_default) && vtk_written; +#else + vtk_written = vtk_writer.write_ASCII (forest_default) && vtk_written; +#endif + /* Check if the writer was successful. */ + T8_ASSERT (vtk_written); + + /* increase the reference counter to avoid deallocation because we reuse the cmesh. */ + t8_cmesh_ref (cmesh); + /* Create a forest using the scheme that we build manually. */ + t8_forest_t forest_manual_scheme = t8_forest_new_uniform (cmesh, t8_scheme_default_build_manually (), 3, 0, comm); + + vtk_written = false; + /* Update the name of the output file. */ + vtk_writer.set_fileprefix (std::string ("forest_with_manual_scheme")); +#if T8_WITH_VTK + vtk_written = vtk_writer.write_with_API (forest_manual_scheme) && vtk_written; +#else + vtk_written = vtk_writer.write_ASCII (forest_manual_scheme) && vtk_written; +#endif + T8_ASSERT (vtk_written); + + /* increase the reference counter because we reuse the cmesh. */ + t8_cmesh_ref (cmesh); + /* Create a forest using a scheme that mixes the default scheme with the stand alone scheme. */ + t8_forest_t forest_mixed_scheme = t8_forest_new_uniform (cmesh, t8_scheme_default_build_mixed (), 3, 0, comm); + + vtk_written = false; + vtk_writer.set_fileprefix (std::string ("forest_with_mixed_scheme")); +#if T8_WITH_VTK + vtk_written = vtk_writer.write_with_API (forest_mixed_scheme) && vtk_written; +#else + vtk_written = vtk_writer.write_ASCII (forest_mixed_scheme) && vtk_written; +#endif + T8_ASSERT (vtk_written); + + /* Clean up the forests. */ + t8_forest_unref (&forest_default); + t8_forest_unref (&forest_manual_scheme); + t8_forest_unref (&forest_mixed_scheme); + + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + + return 0; +} diff --git a/tutorials/general/t8_tutorial_search.cxx b/tutorials/general/t8_tutorial_search.cxx index 2beacc085b..7340e7c894 100644 --- a/tutorials/general/t8_tutorial_search.cxx +++ b/tutorials/general/t8_tutorial_search.cxx @@ -99,11 +99,11 @@ * Those are all leaves in the forest and hence the search will stop after * executing the query callback. * - * Afterwards the search will continue simarly in the second tree. + * Afterwards the search will continue similarly in the second tree. * * Note that the performance of the search could be improved by using an approximative check * on all but the leaf elements. - * This check should return true whenenever a particle is inside an element, but may have false positives. + * This check should return true whenever a particle is inside an element, but may have false positives. * */ @@ -113,25 +113,25 @@ #include /* forest definition and basic interface. */ #include /* save forest */ #include /* default refinement scheme. */ -#include /* Basic operations on 3D vectors. */ +#include /* Basic operations on 3D vectors. */ #include /* For the search algorithm. */ #include /* Example forest adaptation from step 3 */ /* Our search query, a particle together with a flag. */ -typedef struct +struct t8_tutorial_search_particle_t { double coordinates[3]; /* The coordinates of our particle. */ int is_inside_partition; /* Will be set to true if the particles lies inside this process' parallel partition. */ -} t8_tutorial_search_particle_t; +}; /* Additional user data that we process during search. * For each element we count the number of particles that it contains * and we count the total number of elements that we constructed during search. */ -typedef struct +struct t8_tutorial_search_user_data_t { sc_array *particles_per_element; /* For each element the number of particles inside it. */ t8_locidx_t num_elements_searched; /* The total number of elements created. */ -} t8_tutorial_search_user_data_t; +}; /* * The search callback. @@ -305,7 +305,7 @@ t8_tutorial_search_for_particles (t8_forest_t forest, sc_array *particles) sc_array_reset (&particles_per_element); } -/* Create an array of a given number of particles on the root process +/** Create an array of a given number of particles on the root process * and broadcast it to all other processes. * \param [in] num_particles The number of particles to create. * \param [in] seed The seed to be used for the random number generator.