diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 589b267dc..995a37e2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI for Autotools +name: CI for Autotools/Linux on: push: @@ -6,26 +6,34 @@ on: - "**/CMakeLists.txt" - "**.cmake" - "**.cmake.in" + - "cmake/**" + - "CMake*" - ".github/workflows/ci_cmake.yml" + - ".github/workflows/ci_darwin.yml" + - ".github/workflows/ci_valgrind.yml" pull_request: jobs: - linux-multi: + linux-multi1: runs-on: ubuntu-latest - name: Autotools build on Linux + name: Build 1 on Linux steps: + - name: Install system dependencies run: | sudo apt-get update -yq sudo apt-get install -yq --no-install-recommends \ zlib1g-dev libmpich-dev mpich + - name: Checkout source code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: true + - name: Run bootstrap script run: ./bootstrap + - name: Make check with debug, without shared shell: bash run: | @@ -34,7 +42,7 @@ jobs: CFLAGS="-O0 -g -Wall" make -j V=0 make -j check V=0 - cd .. + - name: Make check with MPI and debug shell: bash run: | @@ -43,7 +51,7 @@ jobs: CFLAGS="-O0 -g -Wall" make -j V=0 make -j check V=0 - cd .. + - name: Make check with MPI, without debug shell: bash run: | @@ -52,7 +60,36 @@ jobs: CFLAGS="-O2" make -j V=0 make -j check V=0 - cd .. + + - name: Upload log files + if: always() + uses: actions/upload-artifact@v4 + with: + name: linux_multi1_log + path: | + ./**/config.log + ./**/test-suite.log + ./**/test/*.log + + linux-multi2: + runs-on: ubuntu-latest + name: Build 2 on Linux + steps: + + - name: Install system dependencies + run: | + sudo apt-get update -yq + sudo apt-get install -yq --no-install-recommends \ + zlib1g-dev libmpich-dev mpich + + - name: Checkout source code + uses: actions/checkout@v4 + with: + submodules: true + + - name: Run bootstrap script + run: ./bootstrap + - name: Make check with MPI, debug and C++ compiler shell: bash run: | @@ -61,39 +98,52 @@ jobs: CFLAGS="-O0" CC=mpicxx make -j V=0 make -j check V=0 - cd .. + - name: Make distcheck without MPI and debug shell: bash run: | DIR="distcheck" && mkdir -p "$DIR" && cd "$DIR" ../configure make -j distcheck V=0 - cd .. - - name: Save test suite log + + - name: Upload log files if: always() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: test_suite_log - path: ./**/test-suite.log + name: linux_multi2_log + path: | + ./**/config.log + ./**/test-suite.log + ./**/test/*.log linux-install: runs-on: ubuntu-latest name: Make install on Linux steps: + - name: Install system dependencies run: | sudo apt-get update -yq sudo apt-get install -yq --no-install-recommends \ zlib1g-dev libmpich-dev mpich + - name: Checkout source code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: true - - name: Fix libsc to version 2.8.3 + fetch-depth: 0 + + - name: Fix libsc to specific version shell: bash - run: (cd sc && git fetch --tags && git checkout v2.8.3) + run: | + cd sc + git fetch --tags + git checkout v2.8.6 + # git checkout -b test-branch 2c1496904c485d4ca6f844c396b32f608b72438c + - name: Run bootstrap script run: ./bootstrap + - name: Install libsc with debug, without shared shell: bash run: | @@ -105,6 +155,7 @@ jobs: make -j install V=0 cd .. rm -rf sc/ + - name: Install p4est with debug, without shared shell: bash run: | @@ -115,29 +166,43 @@ jobs: make -j V=0 make -j check V=0 make -j install V=0 - cd .. + + - name: Upload log files + if: always() + uses: actions/upload-artifact@v4 + with: + name: linux_install_log + path: | + ./**/config.log + ./**/test-suite.log + ./**/test/*.log linux-tarball: runs-on: ubuntu-latest name: Pack tarball on Linux steps: + - name: Install system dependencies run: | sudo apt-get update -yq sudo apt-get install -yq --no-install-recommends \ zlib1g-dev libmpich-dev mpich + - name: Checkout source code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 + - name: Identify version shell: bash run: | git tag git describe --abbrev=4 --match 'v*' + - name: Run bootstrap script run: ./bootstrap + - name: Configure and make shell: bash run: | @@ -149,9 +214,19 @@ jobs: make -j check V=0 make -j distcheck V=0 mv p4est-*.tar.gz .. - cd .. - - name: Save tarball - uses: actions/upload-artifact@v2 + + - name: Upload tarball + uses: actions/upload-artifact@v4 with: name: p4est_tarball path: ./p4est-*.tar.gz + + - name: Upload log files + if: always() + uses: actions/upload-artifact@v4 + with: + name: linux_tarball_log + path: | + ./**/config.log + ./**/test-suite.log + ./**/test/*.log diff --git a/.github/workflows/ci_cmake.yml b/.github/workflows/ci_cmake.yml index 273f947c0..582dc8910 100644 --- a/.github/workflows/ci_cmake.yml +++ b/.github/workflows/ci_cmake.yml @@ -1,13 +1,9 @@ name: CI for CMake -env: - CMAKE_BUILD_TYPE: Release - CTEST_PARALLEL_LEVEL: 3 - CMAKE_GENERATOR: Ninja - on: push: paths: + - sc - "**/CMakeLists.txt" - "**.cmake" - "**.cmake.in" @@ -19,157 +15,211 @@ on: release: types: [published] +env: + CTEST_PARALLEL_LEVEL: 4 + CMAKE_BUILD_PARALLEL_LEVEL: 4 + CTEST_NO_TESTS_ACTION: "error" jobs: linux: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 name: CMake build on Linux - timeout-minutes: 15 + timeout-minutes: 60 + + strategy: + matrix: + cc: [gcc-9, gcc-10, gcc-11, gcc-12, gcc-13] + shared: [false] + mpi: [mpich] + include: + - cc: gcc + shared: true + mpi: mpich + - cc: gcc + shared: false + mpi: openmpi + + + env: + CC: ${{ matrix.cc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 name: Checkout source code - name: Install system dependencies run: | sudo apt-get update -yq - sudo apt-get install -yq --no-install-recommends \ - libopenmpi-dev openmpi-bin ninja-build + sudo apt-get install -yq --no-install-recommends zlib1g-dev lib${{ matrix.mpi }}-dev - name: CMake configure - run: cmake -B build --preset default --install-prefix=$HOME + run: >- + cmake --preset default + --install-prefix=${{ runner.temp }} + -DBUILD_SHARED_LIBS:BOOL=${{ matrix.shared }} - name: CMake build - run: cmake --build build - - - name: CMake install (for examples) - run: cmake --install build + run: cmake --build --preset default - name: CMake self-tests - run: ctest --test-dir build --preset default + run: ctest --preset default + + - name: install p4est CMake package + run: cmake --install build + # standalone examples tests that CMake packaging is correct - name: CMake configure examples - run: cmake -B example/build -S example -Dmpi=yes -DP4EST_ROOT=$HOME -DSC_ROOT=$HOME + run: >- + cmake -B example/build -S example + -DCMAKE_PREFIX_PATH:PATH=${{ runner.temp }} + -DBUILD_SHARED_LIBS:BOOL=${{ matrix.shared }} - name: CMake build examples run: cmake --build example/build - - name: CMake test examples - run: ctest --test-dir example/build --output-on-failure - - name: Create package if: github.event.action == 'published' run: cpack --config build/CPackConfig.cmake - name: Upload package if: github.event.action == 'published' - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: - name: binary-archive + name: linux_binary_archive-${{ matrix.cc }}-${{ matrix.mpi }}-shared-${{ matrix.shared }} path: build/package + - name: Upload log files + if: always() + uses: actions/upload-artifact@v4 + with: + name: linux_cmake_log-${{ matrix.cc }}-${{ matrix.mpi }}-shared-${{ matrix.shared }} + path: | + ./build/CMakeFiles/CMakeConfigureLog.yaml + ./build/Testing/Temporary/LastTest.log mac: - runs-on: macos-latest + # macos-14 is to use Apple Silicon hardware + # https://github.blog/changelog/2024-01-30-github-actions-introducing-the-new-m1-macos-runner-available-to-open-source/ + runs-on: macos-14 name: CMake build on MacOS - timeout-minutes: 20 + timeout-minutes: 60 + + strategy: + matrix: + cc: [clang, gcc-13] + shared: [false] + include: + - cc: clang + shared: true + env: HOMEBREW_NO_INSTALL_CLEANUP: 1 + CC: ${{ matrix.cc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 name: Checkout source code - name: Install system dependencies - run: brew install open-mpi ninja + run: brew install open-mpi - name: CMake configure - run: cmake -B build -Dmpi=yes --install-prefix=$HOME + run: >- + cmake --preset default + --install-prefix=${{ runner.temp }} + -DBUILD_SHARED_LIBS:BOOL=${{ matrix.shared }} - name: CMake build - run: cmake --build build - - - name: CMake install (for examples) - run: cmake --install build + run: cmake --build --preset default - name: CMake self-tests - run: ctest --test-dir build --preset default + run: ctest --preset default + + - name: install p4est CMake package + run: cmake --install build - name: CMake configure examples - run: cmake -B example/build -S example -Dmpi=yes -DP4EST_ROOT=$HOME -DSC_ROOT=$HOME + run: >- + cmake -B example/build -S example + -DCMAKE_PREFIX_PATH:PATH=${{ runner.temp }} + -DBUILD_SHARED_LIBS:BOOL=${{ matrix.shared }} - name: CMake build examples run: cmake --build example/build - - name: CMake test examples - run: ctest --test-dir example/build --output-on-failure - - name: Create package if: github.event.action == 'published' run: cpack --config build/CPackConfig.cmake - name: Upload package if: github.event.action == 'published' - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: - name: binary-archive + name: mac_binary_archive-${{ matrix.cc }}-shared-${{ matrix.shared }} path: build/package + - name: Upload log files + if: always() + uses: actions/upload-artifact@v4 + with: + name: mac_cmake_log-${{ matrix.cc }}-shared-${{ matrix.shared }} + path: | + ./build/CMakeFiles/CMakeConfigureLog.yaml + ./build/Testing/Temporary/LastTest.log windows: runs-on: windows-latest name: CMake build on Windows - timeout-minutes: 20 + timeout-minutes: 60 + + strategy: + matrix: + shared: [false] + + env: + CMAKE_GENERATOR: "MinGW Makefiles" steps: - uses: msys2/setup-msys2@v2 with: update: true install: >- - git - mingw-w64-x86_64-cmake - mingw-w64-x86_64-ninja - mingw-w64-x86_64-msmpi + mingw-w64-x86_64-zlib - name: Put MSYS2_MinGW64 on PATH - # there is not yet an environment variable for this path from msys2/setup-msys2 - run: echo "D:/a/_temp/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - - name: Download MS-MPI setup (SDK is from MSYS2) - run: curl -L -O https://github.com/microsoft/Microsoft-MPI/releases/download/v10.1.1/msmpisetup.exe + run: echo "${{ runner.temp }}/msys64/mingw64/bin/" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - name: Install mpiexec.exe (-force needed to bypass GUI on headless) - run: .\msmpisetup.exe -unattend -force - - - name: Put MSMPI_BIN on PATH (where mpiexec is) - run: echo "C:\Program Files\Microsoft MPI\Bin\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - - name: Check file existence - id: check_files - uses: andstor/file-existence-action@v1 - with: - files: "C:/Program Files/Microsoft MPI/Bin/mpiexec.exe" - - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 name: Checkout source code - - name: CMake configure with MPI - if: steps.check_files.outputs.files_exists == 'true' - run: cmake -B build -Dmpi=yes - + # Windows MPI is shaky in general on GitHub Actions, so we don't use it - name: CMake configure without MPI - if: steps.check_files.outputs.files_exists == 'false' - run: cmake -B build -Dmpi=no + run: >- + cmake --preset default + -Dmpi:BOOL=no + --install-prefix=${{ runner.temp }} + -DBUILD_SHARED_LIBS:BOOL=${{ matrix.shared }} + -DZLIB_ROOT:PATH=${{ runner.temp }}/msys64/mingw64/ - name: CMake build - run: cmake --build build + run: cmake --build --preset default - - name: CMake install (for examples) + - name: CMake self-tests + run: ctest --preset default + + - name: install p4est CMake package run: cmake --install build - - name: CMake self-tests - run: ctest --test-dir build --preset default + - name: CMake configure examples + run: >- + cmake -B example/build -S example + -DCMAKE_PREFIX_PATH:PATH=${{ runner.temp }} + -DBUILD_SHARED_LIBS:BOOL=${{ matrix.shared }} + -DZLIB_ROOT:PATH=${{ runner.temp }}/msys64/mingw64/ + + - name: CMake build examples + run: cmake --build example/build - name: Create package if: github.event.action == 'published' @@ -177,7 +227,16 @@ jobs: - name: Upload package if: github.event.action == 'published' - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: - name: binary-archive + name: windows_binary_archive path: build/package + + - name: Upload log files + if: always() + uses: actions/upload-artifact@v4 + with: + name: windows_cmake_log + path: | + ./build/CMakeFiles/CMakeConfigureLog.yaml + ./build/Testing/Temporary/LastTest.log diff --git a/.github/workflows/ci_darwin.yml b/.github/workflows/ci_darwin.yml new file mode 100644 index 000000000..06f3c143d --- /dev/null +++ b/.github/workflows/ci_darwin.yml @@ -0,0 +1,103 @@ +name: CI for Autotools/Darwin + +on: + push: + paths-ignore: + - "**/CMakeLists.txt" + - "**.cmake" + - "**.cmake.in" + - "cmake/**" + - "CMake*" + - ".github/workflows/ci.yml" + - ".github/workflows/ci_cmake.yml" + - ".github/workflows/ci_valgrind.yml" + pull_request: + +jobs: + + darwin1: + runs-on: macos-latest + name: Autotools build 1 on Darwin + env: + HOMEBREW_NO_INSTALL_CLEANUP: 1 + + steps: + - run: echo "This job is running on a ${{ runner.os }} server hosted by GitHub" + + - uses: actions/checkout@v4 + name: Checkout source code + with: + submodules: true + + - name: Install system dependencies + run: brew install open-mpi ninja automake + + - name: Run bootstrap script + run: ./bootstrap + + - name: Make check without MPI, with debug + shell: bash + run: | + DIR="checkdebug" && mkdir -p "$DIR" && cd "$DIR" + ../configure --enable-debug \ + CFLAGS="-O0 -g -Wall -Wextra -Wno-unused-parameter" + make -j V=0 + make -j check V=0 + + - name: Make check with MPI and debug + shell: bash + run: | + DIR="checkMPIdebug" && mkdir -p "$DIR" && cd "$DIR" + ../configure --enable-mpi --enable-debug \ + CFLAGS="-O0 -g -Wall -Wextra -Wno-unused-parameter" + make -j V=0 + make -j check V=0 + + - name: Upload log files + if: always() + uses: actions/upload-artifact@v4 + with: + name: darwin1_log + path: | + ./**/config.log + ./**/test-suite.log + ./**/test/*.log + + darwin2: + runs-on: macos-latest + name: Autotools build 2 on Darwin + env: + HOMEBREW_NO_INSTALL_CLEANUP: 1 + + steps: + - run: echo "This job is running on a ${{ runner.os }} server hosted by GitHub" + + - uses: actions/checkout@v4 + name: Checkout source code + with: + submodules: true + + - name: Install system dependencies + run: brew install open-mpi ninja automake + + - name: Run bootstrap script + run: ./bootstrap + + - name: Make check with MPI, debug and C++ compiler + shell: bash + run: | + DIR="checkMPIdebugCXX" && mkdir -p "$DIR" && cd "$DIR" + ../configure --enable-mpi --enable-debug CC=mpicxx \ + CFLAGS="-O0 -g -Wall -Wextra -Wno-unused-parameter" + make -j V=0 + make -j check V=0 + + - name: Upload log files + if: always() + uses: actions/upload-artifact@v4 + with: + name: darwin2_log + path: | + ./**/config.log + ./**/test-suite.log + ./**/test/*.log diff --git a/.github/workflows/ci_valgrind.yml b/.github/workflows/ci_valgrind.yml new file mode 100644 index 000000000..306b9fd9d --- /dev/null +++ b/.github/workflows/ci_valgrind.yml @@ -0,0 +1,83 @@ +name: CI for valgrind + +on: + push: + paths: + - "src/*.c" + - "test/*.c" + - ".github/workflows/ci_valgrind.yml" + +jobs: + + linux-autotools-valgrind: + runs-on: ubuntu-latest + name: Autotools with valgrind + steps: + + - name: Install system dependencies + run: | + sudo apt-get update -yq + sudo apt-get install -yq --no-install-recommends \ + zlib1g-dev libmpich-dev mpich valgrind + + - name: Checkout source code + uses: actions/checkout@v3 + with: + submodules: true + + - name: Patch valgrind CI, i. e. exclude p8est_test_balance + run: git apply doc/patch/patch-valgrind-CI.patch + + - name: Run bootstrap script + run: ./bootstrap + + - name: Make check with MPI and valgrind, without shared + shell: bash + run: | + DIR="checkMPIvalgrind" && mkdir -p "$DIR" && cd "$DIR" + ../configure --enable-mpi --disable-shared --enable-valgrind \ + CFLAGS="-O2 -Wall -Wextra -Wno-unused-parameter" + make -j V=0 + make -j check V=0 + + - name: Upload log files + if: always() + uses: actions/upload-artifact@v3 + with: + name: linux_autotools_valgrind_log + path: | + ./**/config.log + ./**/test-suite.log + ./**/test/*.log + + linux-cmake-valgrind: + runs-on: ubuntu-latest + name: CMake with valgrind + steps: + + - name: Install system dependencies + run: | + sudo apt-get update -yq + sudo apt-get install -yq --no-install-recommends \ + zlib1g-dev libmpich-dev mpich valgrind + + - name: Checkout source code + uses: actions/checkout@v3 + with: + submodules: true + + - name: CMake build with MPI and valgrind + shell: bash + run: | + DIR="checkCMakeMPIvalgrind" && mkdir -p "$DIR" && cd "$DIR" + cmake -Dmpi=yes -DCMAKE_BUILD_TYPE=Release .. + cmake --build . --parallel + ctest . -T memcheck + + - name: Upload log files + if: always() + uses: actions/upload-artifact@v3 + with: + name: linux_cmake_valgrind_log + path: | + ./checkCMakeMPIvalgrind/Testing/Temporary diff --git a/AUTHORS b/AUTHORS index 9f10afdcd..20f56c3fc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -38,7 +38,7 @@ Ethan Alan Hereth Alex Fikl Hannes Frank - Michael Lahnert + Michael Lahnert Olivier Iffrig */ diff --git a/CMakeLists.txt b/CMakeLists.txt index eabd67c7c..95013ba1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13...3.21) +cmake_minimum_required(VERSION 3.15...3.28) include(cmake/git.cmake) @@ -8,9 +8,7 @@ HOMEPAGE_URL https://www.p4est.org/ DESCRIPTION "p4est manages a collection---a forest---of octrees in parallel." VERSION ${PROJECT_VERSION}) -include(CTest) - -list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules) +enable_testing() # --- user options @@ -20,71 +18,49 @@ message(STATUS "p4est ${PROJECT_VERSION} " # --- external libs -if(mpi) - find_package(MPI COMPONENTS C REQUIRED) - if(NOT MPIEXEC_EXECUTABLE) - message(FATAL_ERROR "MPIEXEC is missing, MPI will not work properly") - endif() -endif() - -find_package(ZLIB) - -# --- libsc -if(NOT sc_external) - find_package(SC) -endif() -if(NOT TARGET SC::SC) - include(cmake/sc.cmake) -endif() +include(cmake/GitSubmodule.cmake) +git_submodule("${PROJECT_SOURCE_DIR}/sc") +add_subdirectory(sc) # --- configure p4est include(cmake/config.cmake) include(cmake/compilers.cmake) -target_link_libraries(SC::SC INTERFACE -$<$:MPI::MPI_C> -$<$:ZLIB::ZLIB> -$<$:m> -) # --- p4est # p4est is always needed. add_library(p4est) -set_target_properties(p4est PROPERTIES EXPORT_NAME P4EST) -target_include_directories(p4est - PRIVATE src ${PROJECT_BINARY_DIR}/include - INTERFACE +set_property(TARGET p4est PROPERTY EXPORT_NAME P4EST) +set_property(TARGET p4est PROPERTY SOVERSION ${P4EST_SOVERSION}) +target_include_directories(p4est PUBLIC $ $ $) -target_link_libraries(p4est PUBLIC SC::SC) +target_link_libraries(p4est PUBLIC SC::SC $<$:${WINSOCK_LIBRARIES}>) -# imported target, for use from FetchContent +# imported target, for use from parent projects add_library(P4EST::P4EST INTERFACE IMPORTED GLOBAL) target_link_libraries(P4EST::P4EST INTERFACE p4est) install(DIRECTORY ${PROJECT_SOURCE_DIR}/src/ ${PROJECT_BINARY_DIR}/include/ - DESTINATION include + TYPE INCLUDE FILES_MATCHING PATTERN "*.h") -install(TARGETS p4est - EXPORT ${PROJECT_NAME}Targets - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) +install(TARGETS p4est EXPORT ${PROJECT_NAME}-targets) #--- p8est -if(enable_p8est) +if(P4EST_ENABLE_BUILD_3D) add_library(p8est OBJECT) target_include_directories(p8est PRIVATE src ${PROJECT_BINARY_DIR}/include) target_link_libraries(p8est PRIVATE SC::SC) target_sources(p4est PRIVATE $) -endif(enable_p8est) +endif() #--- p6est -if(enable_p6est AND enable_p8est) +if(P4EST_ENABLE_BUILD_P6EST AND P4EST_ENABLE_BUILD_3D) add_library(p6est OBJECT) target_include_directories(p6est PRIVATE src ${PROJECT_BINARY_DIR}/include) target_link_libraries(p6est PRIVATE SC::SC) @@ -95,23 +71,23 @@ add_subdirectory(src) # --- optional test and install -if(BUILD_TESTING) +if(P4EST_BUILD_TESTING) add_subdirectory(test) -endif(BUILD_TESTING) +endif() # --- packaging -install(FILES - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/FindSC.cmake - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/FindP4EST.cmake - DESTINATION lib/cmake/Modules) - include(cmake/pkgconf.cmake) include(cmake/install.cmake) +# --- build examples +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.22) + add_subdirectory(example) +endif() + include(FeatureSummary) -add_feature_info(MPI mpi "MPI features of ${PROJECT_NAME}") -add_feature_info(OpenMP openmp "OpenMP features of ${PROJECT_NAME}") -add_feature_info(P6EST enable_p6est "2D-3D p6est") -add_feature_info(P8EST enable_p8est "3D p8est") +add_feature_info(MPI P4EST_ENABLE_MPI "MPI features of ${PROJECT_NAME}") +add_feature_info(P6EST P4EST_ENABLE_BUILD_P6EST "2D-3D p6est") +add_feature_info(P8EST P4EST_ENABLE_BUILD_3D "3D p8est") +add_feature_info(shared BUILD_SHARED_LIBS "Build shared ${PROJECT_NAME} libraries") feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES) diff --git a/CMakePresets.json b/CMakePresets.json index e1b6fea91..ae9bd8864 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,27 +1,56 @@ { - "version": 3, + "version": 6, "configurePresets": [ { "name": "default", + "binaryDir": "${sourceDir}/build", "cacheVariables": { "mpi": true, - "openmp": true + "openmp": false, + "CMAKE_COMPILE_WARNING_AS_ERROR": true } } ], +"buildPresets": [ + { + "name": "default", + "configurePreset": "default" + } +], "testPresets": [ { "name": "default", "configurePreset": "default", "output": { "outputOnFailure": true, - "verbosity": "default" + "verbosity": "verbose" }, "execution": { "noTestsAction": "error", - "stopOnFailure": false + "scheduleRandom": true, + "stopOnFailure": false, + "timeout": 60 } } +], +"workflowPresets": [ + { + "name": "default", + "steps": [ + { + "type": "configure", + "name": "default" + }, + { + "type": "build", + "name": "default" + }, + { + "type": "test", + "name": "default" + } + ] + } ] } diff --git a/Makefile.am b/Makefile.am index c426a1fe9..8e43a7c82 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,19 +2,19 @@ # This file is part of p4est. # Makefile.am in toplevel directory -ACLOCAL_AMFLAGS = -I config -if P4EST_SC_MK_USE -@P4EST_SC_MK_INCLUDE@ -endif +ACLOCAL_AMFLAGS = -I config @P4EST_SC_AMFLAGS@ +## if P4EST_SC_MK_USE +## @P4EST_SC_MK_INCLUDE@ +## endif # initialize empty variables AM_CPPFLAGS = +AM_LDFLAGS = BUILT_SOURCES = CLEANFILES = DISTCLEANFILES = EXTRA_DIST = LDADD = -LINT_CSOURCES = TESTS = bin_PROGRAMS = check_PROGRAMS = @@ -46,10 +46,8 @@ dist_p4estaclocal_DATA = \ p4estdatadir = $(datadir)/data # setup test environment -if P4EST_MPIRUN -LOG_COMPILER = @P4EST_MPIRUN@ -AM_LOG_FLAGS = @P4EST_MPI_TEST_FLAGS@ -endif +LOG_COMPILER = @P4EST_MPIRUN@ @P4EST_MPI_TEST_FLAGS@ \ + @P4EST_VALGRIND@ @P4EST_VALGRIND_FLAGS@ # recurse only into subpackages SUBDIRS = @P4EST_SC_SUBDIR@ @@ -76,28 +74,8 @@ include example/spheres/Makefile.am include example/steps/Makefile.am include example/tetgen/Makefile.am include example/timings/Makefile.am -include example/balance_seeds/Makefile.am - -## This was only used for our lint code, which needs to be replaced. -## -## # lint static syntax checker -## ALL_LINT_FLAGS = $(LINT_FLAGS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ -## $(MPI_INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) \ -## $(src_libp4est_a_CPPFLAGS) -## lint: -## if LINT -## @for subdir in $(SUBDIRS) ; do \ -## echo "Making $@ in $$subdir"; \ -## (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) lint) ; \ -## done -## for f in $(LINT_CSOURCES) ; do \ -## $(LINT) $(ALL_LINT_FLAGS) $(top_srcdir)/$$f || \ -## echo "Lint check failed for $$f" ; \ -## done -## else -## @echo "Static source code check disabled by configure" -## endif -## .PHONY: lint +include example/balance/Makefile.am +include example/search/Makefile.am # revision control and ChangeLog ChangeLog: @@ -125,17 +103,31 @@ endif install-data-hook: cd $(DESTDIR)$(pkgconfigdir) && \ mv -f p4est_autotools.pc "p4est-$(VERSION).pc" + cd $(DESTDIR)$(pkgconfigdir) && ln -sf "p4est-$(VERSION).pc" p4est.pc uninstall-hook: cd $(DESTDIR)$(pkgconfigdir) && rm -f "p4est-$(VERSION).pc" + cd $(DESTDIR)$(pkgconfigdir) && rm -f "p4est.pc" + +## From the GNU automake documentation "What gets Cleaned" +## ------------------------------------------------------- +## If make built it, and it is commonly something that one would want to rebuild +## (for instance, a .o file), then mostlyclean should delete it. +## Otherwise, if make built it, then clean should delete it. clean-local: - rm -f ChangeLog *vtu *.visit *.p4c *.p4p *.p8c *.p8p + rm -f ChangeLog *vtu *.visit *.p4c *.p4p *.p8c *.p8p *.p4d *.p8d + +## If configure built it, then distclean should delete it. +distclean-local: +## If the maintainer built it (for instance, a .info file), then +## maintainer-clean should delete it. However maintainer-clean should not delete +## anything that needs to exist in order to run ‘./configure && make’. maintainer-clean-local: - rm -rf doxygen + rm -rf doc/{html,latex,man,*.doxygen.tags} -doxygen: Doxyfile - doxygen +doxygen-local: doc/Doxyfile + cd doc && doxygen Doxyfile .PHONY: doxygen diff --git a/README b/README index 62e5a9d86..eae39202f 100644 --- a/README +++ b/README @@ -169,6 +169,10 @@ necessary configuration files and the configure script. A bootstrap script is provided for this purpose: give the shell command `./bootstrap` before following the instructions in the `INSTALL` file. Then proceed as in 5. +Note that `autoreconf` does not work in the source directory. +It is trying to grep `ACLOCAL_AMFLAGS` from the `Makefile.am`. +We use this variable for substitution, which autoreconf cannot survive. + 7. Installation from git repository diff --git a/bootstrap b/bootstrap index 6698a45c9..49b9d0ea4 100755 --- a/bootstrap +++ b/bootstrap @@ -28,9 +28,19 @@ if test -x "sc/bootstrap" ; then (cd sc && ./bootstrap) fi -echo "--- This is the bootstrap script for p4est calling autoreconf ---" +echo "--- This is the bootstrap script for p4est ---" echo "It is NOT required to run bootstrap to build from a tar.gz archive" echo "Current directory is $PWD" rm -rf autom4te.cache -autoreconf -i -I "$SC_CONFIG" + +LIBTOOLIZE=`which glibtoolize` +if test ! -x "$LIBTOOLIZE" ; then LIBTOOLIZE=`which libtoolize` ; fi +if test ! -x "$LIBTOOLIZE" ; then echo "bootstrap requires libtoolize" ; \ + exit 1 ; fi + +aclocal -Wall -I config -I "$SC_CONFIG" +autoconf -Wall --force +autoheader -Wall --force +"$LIBTOOLIZE" --install --copy +automake -Wall --add-missing --copy diff --git a/cmake/.cpack_ignore b/cmake/.cpack_ignore deleted file mode 100644 index adbd31d9e..000000000 --- a/cmake/.cpack_ignore +++ /dev/null @@ -1,26 +0,0 @@ -\\.git/ -\\.git* -\\.vscode/ -_CPack_Packages/ -Makefile.in -aclocal.m4 -autom4te.cache/ -build/ -build-aux/ar-lib -build-aux/compile -build-aux/config.guess -build-aux/config.sub -build-aux/depcomp -build-aux/install-sh -build-aux/ltmain.sh -build-aux/missing -build-aux/test-driver -config/libtool.m4 -config/ltoptions.m4 -config/ltsugar.m4 -config/ltversion.m4 -config/lt~obsolete.m4 -configure -src/sc_config_autotools.h.in -scindent -scspell diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in deleted file mode 100644 index a3ddbe82e..000000000 --- a/cmake/Config.cmake.in +++ /dev/null @@ -1,11 +0,0 @@ -@PACKAGE_INIT@ - -include(CMakeFindDependencyMacro) - -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../Modules) -find_dependency(SC) -find_dependency(ZLIB) - -include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") - -check_required_components(@PROJECT_NAME@) diff --git a/cmake/GitSubmodule.cmake b/cmake/GitSubmodule.cmake index 9e98b0c29..4f2d56726 100644 --- a/cmake/GitSubmodule.cmake +++ b/cmake/GitSubmodule.cmake @@ -1,17 +1,28 @@ -function(git_submodule dir) +# else it's an offline archive +if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/.git) + find_package(Git REQUIRED) +endif() + +function(git_submodule submod_dir) # get/update Git submodule directory to CMake, assuming the # Git submodule directory is a CMake project. -find_package(Git REQUIRED) +# EXISTS because Git submodules have .git as a file, not directory +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git) + message(DEBUG "${PROJECT_SOURCE_DIR} is not a Git repository, skipping submodule ${submod_dir}") + return() +endif() + +if(EXISTS ${submod_dir}/CMakeLists.txt) + return() +endif() -if(NOT EXISTS ${dir}/CMakeLists.txt) - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive -- ${dir} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE _err) +execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive -- ${submod_dir} +WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} +RESULT_VARIABLE err) - if(NOT _err EQUAL 0) - message(SEND_ERROR "Could not retrieve Git submodule ${dir}.") - endif() +if(NOT err EQUAL 0) + message(FATAL_ERROR "${submod_dir} Git submodule failed to retrieve.") endif() -endfunction(git_submodule) +endfunction() diff --git a/cmake/Modules/FindP4EST.cmake b/cmake/Modules/FindP4EST.cmake deleted file mode 100644 index 5e2463b0a..000000000 --- a/cmake/Modules/FindP4EST.cmake +++ /dev/null @@ -1,48 +0,0 @@ -# FindP4EST.cmake -# --------------- -# -# Find p4est library -# -# Result Variables -# ---------------- -# -# This module defines the following variables:: -# -# P4EST_FOUND -# P4EST_INCLUDE_DIRS - include directories for p4est -# P4EST_LIBRARIES - link against this library to use p4est -# -# Imported Targets -# ^^^^^^^^^^^^^^^^ -# P4EST::P4EST - - -find_path (P4EST_INCLUDE_DIR - NAMES p4est.h - DOC "p4est header") - -find_library (P4EST_LIBRARY - NAMES p4est - DOC "p4est library") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args (P4EST - REQUIRED_VARS P4EST_LIBRARY P4EST_INCLUDE_DIR - ) - -if(P4EST_FOUND) - -set(P4EST_INCLUDE_DIRS ${P4EST_INCLUDE_DIR}) -set(P4EST_LIBRARIES ${P4EST_LIBRARY}) - -if(NOT TARGET P4EST::P4EST) - add_library(P4EST::P4EST INTERFACE IMPORTED) - set_target_properties(P4EST::P4EST PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${P4EST_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "${P4EST_LIBRARY}" - ) -endif() - -endif() - -mark_as_advanced(P4EST_INCLUDE_DIR P4EST_LIBRARY) diff --git a/cmake/Modules/FindSC.cmake b/cmake/Modules/FindSC.cmake deleted file mode 100644 index 113bedc99..000000000 --- a/cmake/Modules/FindSC.cmake +++ /dev/null @@ -1,75 +0,0 @@ -# FindSC.cmake -# --------------- -# -# Find libsc library -# -# Result Variables -# ---------------- -# -# This module defines the following variables:: -# -# SC_FOUND -# SC_INCLUDE_DIRS - include directories for libsc -# SC_LIBRARIES - link against this library to use libsc -# -# Imported Targets -# ^^^^^^^^^^^^^^^^ -# SC::SC - - -find_path (SC_INCLUDE_DIR - NAMES sc.h - DOC "libsc header") - -find_library (SC_LIBRARY - NAMES sc - DOC "libsc library") - - -if(SC_INCLUDE_DIR AND SC_LIBRARY) - set(SC_mpi_ok true) - - # check if libsc was configured in compatible way - include(CheckSymbolExists) - set(CMAKE_REQUIRED_FLAGS) - set(CMAKE_REQUIRED_INCLUDES) - set(CMAKE_REQUIRED_LIBRARIES) - - # libsc and current project must both be compiled with/without MPI - check_symbol_exists(SC_ENABLE_MPI ${SC_INCLUDE_DIR}/sc_config.h SC_has_mpi) - check_symbol_exists(SC_ENABLE_MPIIO ${SC_INCLUDE_DIR}/sc_config.h SC_has_mpi_io) - - if(MPI_C_FOUND) - # a sign the current project is using MPI - if(NOT (SC_has_mpi AND SC_has_mpi_io)) - set(SC_mpi_ok false) - endif() - else() - if(SC_has_mpi OR SC_has_mpi_io) - set(SC_mpi_ok false) - endif() - endif() - -endif() - - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args (SC - REQUIRED_VARS SC_LIBRARY SC_INCLUDE_DIR SC_mpi_ok) - -if(SC_FOUND) - -set(SC_INCLUDE_DIRS ${SC_INCLUDE_DIR}) -set(SC_LIBRARIES ${SC_LIBRARY}) - -if(NOT TARGET SC::SC) - add_library(SC::SC INTERFACE IMPORTED) - - set_target_properties(SC::SC PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${SC_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "${SC_LIBRARY}") -endif() - -endif(SC_FOUND) - -mark_as_advanced(SC_INCLUDE_DIR SC_LIBRARY) diff --git a/cmake/compilers.cmake b/cmake/compilers.cmake index 699f45105..06d9dc211 100644 --- a/cmake/compilers.cmake +++ b/cmake/compilers.cmake @@ -1,13 +1,9 @@ -include(CheckCCompilerFlag) - -# --- compiler options - -check_c_compiler_flag(-Wall _has_wall) -if(_has_wall) - add_compile_options(-Wall) +if(MSVC) + add_compile_options(/W4) else() - check_c_compiler_flag(/Wall _has_msvc_wall) - if(_has_msvc_wall) - add_compile_options(/Wall) - endif() + add_compile_options(-Wall + $<$:-Wno-unused-but-set-variable>) endif() + +# disable nuisance warnings from Visual Studio +add_compile_definitions($<$:_CRT_SECURE_NO_WARNINGS>) diff --git a/cmake/config.cmake b/cmake/config.cmake index ee473f9e4..2b6710767 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -2,6 +2,13 @@ include(CheckIncludeFile) include(CheckSymbolExists) include(ProcessorCount) +# --- retrieve library interface version from configuration file +file(STRINGS config/p4est_soversion.in P4EST_SOVERSION_READ + REGEX "^[ \t]*P4EST_SOVERSION *= *[0-9:]+") +string(REGEX REPLACE ".*= *([0-9]+):([0-9]+):([0-9]+)" "\\1.\\2.\\3" + P4EST_SOVERSION ${P4EST_SOVERSION_READ}) +message(STATUS "p4est SOVERSION configured as ${P4EST_SOVERSION}") + # on some platforms e.g. ARM, we have to try a few ways to get CPU count > 1 for multi-core systems cmake_host_system_information(RESULT Ncpu QUERY NUMBER_OF_PHYSICAL_CORES) if(Ncpu LESS 2) @@ -9,14 +16,31 @@ if(Ncpu LESS 2) if(n GREATER Ncpu) set(Ncpu ${n}) endif() + set(MPIEXEC_NUMPROC_MAX 1) +else() + set(MPIEXEC_NUMPROC_MAX 2) endif() +# --- set global compile environment + +# Build all targets with -fPIC so that libsc itself can be linked as a +# shared library, or linked into a shared library. +include(CheckPIESupported) +check_pie_supported() +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + # --- generate p4est_config.h set(CMAKE_REQUIRED_INCLUDES) set(CMAKE_REQUIRED_LIBRARIES) -if(MPI_FOUND) +set(P4EST_ENABLE_MPI ${SC_ENABLE_MPI} CACHE BOOL "P4EST MPI support" FORCE) +set(P4EST_ENABLE_MPIIO ${SC_ENABLE_MPIIO} CACHE BOOL "P4EST MPI-IO support" FORCE) +set(P4EST_HAVE_ZLIB ${SC_HAVE_ZLIB} CACHE BOOL "P4EST zlib support" FORCE) +set(P4EST_HAVE_JSON ${SC_HAVE_JSON} CACHE BOOL "P4EST json support" FORCE) + +if(P4EST_ENABLE_MPI) + find_package(MPI REQUIRED COMPONENTS C) set(CMAKE_REQUIRED_LIBRARIES MPI::MPI_C) set(P4EST_CC \"${MPI_C_COMPILER}\") set(P4EST_CPP ${MPI_C_COMPILER}) @@ -32,39 +56,35 @@ endif() string(APPEND P4EST_CPP " -E") set(P4EST_CPP \"${P4EST_CPP}\") -set(P4EST_CFLAGS "${CMAKE_C_FLAGS} ${MPI_C_COMPILE_OPTIONS}") +set(P4EST_CFLAGS "${CMAKE_C_FLAGS}\ ${MPI_C_COMPILE_OPTIONS}") set(P4EST_CFLAGS \"${P4EST_CFLAGS}\") set(P4EST_CPPFLAGS \"\") -set(P4EST_FFLAGS "${CMAKE_Fortran_FLAGS} ${MPI_Fortran_COMPILE_OPTIONS}") +set(P4EST_FFLAGS "${CMAKE_Fortran_FLAGS}\ ${MPI_Fortran_COMPILE_OPTIONS}") set(P4EST_FFLAGS \"${P4EST_FFLAGS}\") set(P4EST_FLIBS \"${MPI_Fortran_LIBRARIES}\") set(P4EST_LDFLAGS \"${MPI_C_LINK_FLAGS}\") -set(P4EST_LIBS \"${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} ${ZLIB_LIBRARIES} m\") +set(P4EST_LIBS \"${LAPACK_LIBRARIES}\ ${BLAS_LIBRARIES}\ ${ZLIB_LIBRARIES}\ m\") + +set(P4EST_ENABLE_BUILD_2D 1 CACHE BOOL "p4est is always used") +set(P4EST_ENABLE_BUILD_3D ${enable_p8est} CACHE BOOL "p8est support" FORCE) +set(P4EST_ENABLE_BUILD_P6EST ${enable_p6est} CACHE BOOL "p6est support" FORCE) -set(P4EST_ENABLE_BUILD_2D true CACHE BOOL "p4est is always used") -set(P4EST_ENABLE_BUILD_3D ${enable_p8est}) -set(P4EST_ENABLE_BUILD_P6EST ${enable_p6est}) +set(P4EST_ENABLE_FILE_DEPRECATED ${enable-file-deprecated}) set(P4EST_ENABLE_MEMALIGN 1) -if(MPI_FOUND) - set(P4EST_ENABLE_MPI ${MPI_FOUND}) - check_symbol_exists(MPI_COMM_TYPE_SHARED mpi.h P4EST_ENABLE_MPICOMMSHARED) - set(P4EST_ENABLE_MPIIO 1) - check_symbol_exists(MPI_Init_thread mpi.h P4EST_ENABLE_MPITHREAD) - check_symbol_exists(MPI_Win_allocate_shared mpi.h P4EST_ENABLE_MPIWINSHARED) -endif(MPI_FOUND) - -check_symbol_exists(sqrt math.h P4EST_NONEED_M) -if(NOT P4EST_NONEED_M) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} m) - check_symbol_exists(sqrt math.h P4EST_NEED_M) +if(P4EST_ENABLE_MPI) + set(P4EST_ENABLE_MPICOMMSHARED ${SC_ENABLE_MPICOMMSHARED} CACHE BOOL "P4EST MPI shared memory support" FORCE) + set(P4EST_ENABLE_MPITHREAD ${SC_ENABLE_MPITHREAD} CACHE BOOL "P4EST MPI thread support" FORCE) + set(P4EST_ENABLE_MPIWINSHARED ${SC_ENABLE_MPIWINSHARED} CACHE BOOL "P4EST MPI window shared memory support" FORCE) endif() +set(P4EST_NEED_M ${SC_NEED_M} CACHE BOOL "P4EST needs math -lm") + check_include_file(arpa/inet.h P4EST_HAVE_ARPA_INET_H) check_include_file(netinet/in.h P4EST_HAVE_NETINET_IN_H) if(WIN32 AND NOT P4EST_HAVE_ARPA_INET_H AND NOT P4EST_HAVE_NETINET_IN_H) @@ -72,33 +92,37 @@ if(WIN32 AND NOT P4EST_HAVE_ARPA_INET_H AND NOT P4EST_HAVE_NETINET_IN_H) set(WINSOCK_LIBRARIES wsock32 ws2_32) # Iphlpapi endif() -check_include_file(dlfcn.h P4EST_HAVE_DLFCN_H) - - -check_include_file(inttypes.h P4EST_HAVE_INTTYPES_H) +set(P4EST_HAVE_INTTYPES_H ${SC_HAVE_INTTYPES_H} CACHE BOOL "platform has inttypes.h") check_symbol_exists(pthread_create pthread.h HAVE_LPTHREAD) check_symbol_exists(lua_createtable lua.h HAVE_LUA) -check_include_file(memory.h P4EST_HAVE_MEMORY_H) +set(P4EST_HAVE_MEMORY_H ${SC_HAVE_MEMORY_H} CACHE BOOL "platform has memory.h") -check_symbol_exists(posix_memalign stdlib.h P4EST_HAVE_POSIX_MEMALIGN) -check_include_file(stdint.h P4EST_HAVE_STDINT_H) -check_include_file(stdlib.h P4EST_HAVE_STDLIB_H) -check_include_file(strings.h P4EST_HAVE_STRINGS_H) -check_include_file(string.h P4EST_HAVE_STRING_H) -check_include_file(sys/stat.h P4EST_HAVE_SYS_STAT_H) -check_include_file(sys/types.h P4EST_HAVE_SYS_TYPES_H) - -check_include_file(unistd.h P4EST_HAVE_UNISTD_H) -if(P4EST_HAVE_UNISTD_H) - check_symbol_exists(fsync unistd.h P4EST_HAVE_FSYNC) - check_include_file(getopt.h P4EST_HAVE_GETOPT_H) -endif() +set(P4EST_HAVE_POSIX_MEMALIGN ${SC_HAVE_POSIX_MEMALIGN} CACHE BOOL "platform has posix_memalign") +set(P4EST_HAVE_STDINT_H ${SC_HAVE_STDINT_H} CACHE BOOL "platform has stdint.h") +set(P4EST_HAVE_STDLIB_H ${SC_HAVE_STDLIB_H} CACHE BOOL "platform has stdlib.h") -if(ZLIB_FOUND) +check_include_file(strings.h P4EST_HAVE_STRINGS_H) +set(P4EST_HAVE_STRING_H ${SC_HAVE_STRING_H} CACHE BOOL "platform has string.h") +set(P4EST_HAVE_SYS_STAT_H ${SC_HAVE_SYS_STAT_H} CACHE BOOL "platform has sys/stat.h") +set(P4EST_HAVE_SYS_TYPES_H ${SC_HAVE_SYS_TYPES_H} CACHE BOOL "platform has sys/types.h") + +set(P4EST_HAVE_UNISTD_H ${SC_HAVE_UNISTD_H} CACHE BOOL "platform has unistd.h") +set(P4EST_HAVE_FSYNC ${SC_HAVE_FSYNC} CACHE BOOL "platform has fsync") +set(P4EST_HAVE_GETOPT_H ${SC_HAVE_GETOPT_H} CACHE BOOL "platform has getopt.h") + +if(P4EST_HAVE_ZLIB) + # ZLIB::ZLIB would be defined in sc/cmake/zlib.cmake IMPORTED INTERFACE GLOBAL via libsc + if(NOT TARGET ZLIB::ZLIB) + # libsc didn't build Zlib, so must find an existing Zlib + find_package(ZLIB REQUIRED) + endif() set(CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB) check_symbol_exists(adler32_combine zlib.h P4EST_HAVE_ZLIB) + if(vtk_binary) + set(P4EST_ENABLE_VTK_COMPRESSION 1 CACHE BOOL "p4est VTK compression support") + endif() endif() if(CMAKE_BUILD_TYPE MATCHES "Debug") diff --git a/cmake/config.cmake.in b/cmake/config.cmake.in new file mode 100644 index 000000000..ce7f35fed --- /dev/null +++ b/cmake/config.cmake.in @@ -0,0 +1,31 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +find_dependency(SC CONFIG) +find_dependency(ZLIB) + +set(P4EST_ENABLE_P6EST @enable_p6est@) +set(P4EST_ENABLE_P8EST @enable_p8est@) + +set(P4EST_ENABLE_MPI @P4EST_ENABLE_MPI@) +set(P4EST_ENABLE_VTK_COMPRESSION @P4EST_ENABLE_VTK_COMPRESSION@) +set(P4EST_HAVE_WINSOCK2_H @P4EST_HAVE_WINSOCK2_H@) +set(P4EST_NEED_M @P4EST_NEED_M@) +set(P4EST_HAVE_GETOPT_H @P4EST_HAVE_GETOPT_H@) + +if(P4EST_HAVE_WINSOCK2_H) + target_link_libraries(SC::SC INTERFACE wsock32 ws2_32) +endif() + +if(P4EST_ENABLE_MPI) + find_dependency(MPI COMPONENTS C) +endif() + +if(P4EST_HAVE_JSON) + find_dependency(jansson CONFIG) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") + +check_required_components(@PROJECT_NAME@) diff --git a/cmake/git.cmake b/cmake/git.cmake index dd6009e43..4718b8e9d 100644 --- a/cmake/git.cmake +++ b/cmake/git.cmake @@ -1,10 +1,13 @@ # --- extract version from Git +set(PROJECT_MAJOR 0) +set(PROJECT_MINOR 0) +set(PROJECT_PATCH 0) set(PROJECT_VERSION 0.0.0) find_program(GIT_VERSION_GEN NAMES git-version-gen - PATHS ${CMAKE_SOURCE_DIR}/build-aux NO_DEFAULT_PATH) + PATHS ${PROJECT_SOURCE_DIR}/build-aux NO_DEFAULT_PATH) if(GIT_VERSION_GEN) - execute_process(COMMAND ${GIT_VERSION_GEN} .tarball_version + execute_process(COMMAND ${GIT_VERSION_GEN} .tarball-version WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE _err OUTPUT_VARIABLE git_version @@ -13,24 +16,24 @@ endif() if(_err EQUAL 0) if(git_version MATCHES "^(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.].*") - set(_major "${CMAKE_MATCH_1}") - set(_minor "${CMAKE_MATCH_2}") - set(_patch "${CMAKE_MATCH_3}") - set(PROJECT_VERSION ${_major}.${_minor}.${_patch}.999) + set(PROJECT_MAJOR "${CMAKE_MATCH_1}") + set(PROJECT_MINOR "${CMAKE_MATCH_2}") + set(PROJECT_PATCH "${CMAKE_MATCH_3}") + set(PROJECT_VERSION ${PROJECT_MAJOR}.${PROJECT_MINOR}.${PROJECT_PATCH}.999) elseif(git_version MATCHES "^(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)") - set(_major "${CMAKE_MATCH_1}") - set(_minor "${CMAKE_MATCH_2}") - set(_patch "${CMAKE_MATCH_3}") - set(PROJECT_VERSION ${_major}.${_minor}.${_patch}) + set(PROJECT_MAJOR "${CMAKE_MATCH_1}") + set(PROJECT_MINOR "${CMAKE_MATCH_2}") + set(PROJECT_PATCH "${CMAKE_MATCH_3}") + set(PROJECT_VERSION ${PROJECT_MAJOR}.${PROJECT_MINOR}.${PROJECT_PATCH}) elseif(git_version MATCHES "^(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)") - set(_major "${CMAKE_MATCH_1}") - set(_minor "${CMAKE_MATCH_2}") - set(PROJECT_VERSION ${_major}.${_minor}) + set(PROJECT_MAJOR "${CMAKE_MATCH_1}") + set(PROJECT_MINOR "${CMAKE_MATCH_2}") + set(PROJECT_VERSION ${PROJECT_MAJOR}.${PROJECT_MINOR}) elseif(git_version MATCHES "^(0|[1-9][0-9]*)") - set(_major "${CMAKE_MATCH_1}") - set(PROJECT_VERSION ${_major}) + set(PROJECT_MAJOR "${CMAKE_MATCH_1}") + set(PROJECT_VERSION ${PROJECT_MAJOR}) endif() endif() diff --git a/cmake/install.cmake b/cmake/install.cmake index bdce32736..f4f92c499 100644 --- a/cmake/install.cmake +++ b/cmake/install.cmake @@ -2,48 +2,74 @@ include(CMakePackageConfigHelpers) -configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/Config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake - INSTALL_DESTINATION lib) +configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/config.cmake.in +${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}Config.cmake +INSTALL_DESTINATION cmake +) write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake - COMPATIBILITY SameMinorVersion) +${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}ConfigVersion.cmake +COMPATIBILITY SameMajorVersion +) -install(EXPORT ${PROJECT_NAME}Targets - NAMESPACE ${PROJECT_NAME}:: - DESTINATION lib/cmake/${PROJECT_NAME}) +install(EXPORT ${PROJECT_NAME}-targets +NAMESPACE ${PROJECT_NAME}:: +DESTINATION cmake +) install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake - DESTINATION lib/cmake/${PROJECT_NAME}) +${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}Config.cmake +${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}ConfigVersion.cmake +DESTINATION cmake +) # --- CPack - -set(_fmt TGZ) if(WIN32) - set(_fmt ZIP) + set(CPACK_GENERATOR "ZIP") + set(CPACK_SOURCE_GENERATOR "ZIP") +else() + set(CPACK_GENERATOR "TGZ") + set(CPACK_SOURCE_GENERATOR "TGZ") endif() -set(CPACK_GENERATOR ${_fmt}) -set(CPACK_SOURCE_GENERATOR ${_fmt}) set(CPACK_PACKAGE_VENDOR "Carsten Burstedde") set(CPACK_PACKAGE_CONTACT "Carsten Burstedde") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README") -set(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/package") -set(CPACK_PACKAGE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +set(CPACK_PACKAGE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/package) string(TOLOWER ${CMAKE_SYSTEM_NAME} _sys) string(TOLOWER ${PROJECT_NAME} _project_lower) set(CPACK_PACKAGE_FILE_NAME "${_project_lower}-${git_version}-${_sys}") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${_project_lower}-${git_version}") # not .gitignore as its regex syntax is more advanced than CMake -file(READ ${CMAKE_CURRENT_LIST_DIR}/.cpack_ignore _cpack_ignore) -string(REGEX REPLACE "\n" ";" _cpack_ignore ${_cpack_ignore}) -set(CPACK_SOURCE_IGNORE_FILES "${_cpack_ignore}") +set(CPACK_SOURCE_IGNORE_FILES .git/ .github/ .vscode/ _CPack_Packages/ +${CMAKE_BINARY_DIR}/ ${PROJECT_BINARY_DIR}/ +Makefile.in +aclocal.m4 +autom4te.cache/ +build/ +build-aux/ar-lib +build-aux/compile +build-aux/config.guess +build-aux/config.sub +build-aux/depcomp +build-aux/install-sh +build-aux/ltmain.sh +build-aux/missing +build-aux/test-driver +config/libtool.m4 +config/ltoptions.m4 +config/ltsugar.m4 +config/ltversion.m4 +config/lt~obsolete.m4 +configure +src/sc_config_autotools.h.in +scindent +scspell +) install(FILES ${CPACK_RESOURCE_FILE_README} ${CPACK_RESOURCE_FILE_LICENSE} - DESTINATION share/docs/${PROJECT_NAME}) +DESTINATION share/docs/${PROJECT_NAME} +) include(CPack) diff --git a/cmake/options.cmake b/cmake/options.cmake index 2fd168740..477c178f8 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -1,30 +1,33 @@ option(enable_p6est "build p6est" on) option(enable_p8est "build p8est" on) -option(vtk "VTK interface" on) +option(P4EST_BUILD_TESTING "build p4est testing" on) -option(mpi "use MPI library" off) -option(openmp "use OpenMP" off) +option(enable-file-deprecated "use deprecated data file format" off) -option(sc_external "force build of libsc") - -set(CMAKE_EXPORT_COMPILE_COMMANDS on) -set(CMAKE_TLS_VERIFY on) - -if(dev) - -else() - set_directory_properties(PROPERTIES EP_UPDATE_DISCONNECTED true) +option(vtk_binary "VTK binary interface" on) +if(vtk_binary) + set(P4EST_ENABLE_VTK_BINARY 1) endif() # --- default install directory under build/local # users can specify like "cmake -B build -DCMAKE_INSTALL_PREFIX=~/mydir" -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) +if(CMAKE_VERSION VERSION_LESS 3.21) + get_property(_not_top DIRECTORY PROPERTY PARENT_DIRECTORY) + if(NOT _not_top) + set(P4EST_IS_TOP_LEVEL true) + endif() +endif() + +if(P4EST_IS_TOP_LEVEL AND CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) # will not take effect without FORCE set(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/local" CACHE PATH "Install top-level directory" FORCE) endif() +# Necessary for shared library with Visual Studio / Windows oneAPI +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS true) + # --- auto-ignore build directory -if(NOT EXISTS ${PROJECT_BINARY_DIR}/.gitignore) - file(WRITE ${PROJECT_BINARY_DIR}/.gitignore "*") +if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + file(GENERATE OUTPUT .gitignore CONTENT "*") endif() diff --git a/cmake/p4est_config.h.in b/cmake/p4est_config.h.in index 72645ddb6..e95b98389 100644 --- a/cmake/p4est_config.h.in +++ b/cmake/p4est_config.h.in @@ -12,42 +12,44 @@ /* C preprocessor flags */ #define P4EST_CPPFLAGS @P4EST_CPPFLAGS@ -/* Undefine if: disable the 2D library */ -#cmakedefine P4EST_ENABLE_BUILD_2D +/* Define to 1 if the 2D library is enabled */ +#cmakedefine P4EST_ENABLE_BUILD_2D 1 -/* Undefine if: disable the 3D library */ -#cmakedefine P4EST_ENABLE_BUILD_3D +/* Define to 1 if the 3D library is enabled */ +#cmakedefine P4EST_ENABLE_BUILD_3D 1 -/* Undefine if: disable hybrid 2D+1D p6est library */ -#cmakedefine P4EST_ENABLE_BUILD_P6EST +/* Define to 1 if the hybrid 2D+1D p6est library enabled */ +#cmakedefine P4EST_ENABLE_BUILD_P6EST 1 /* Define to 1 if we are using debug build type (assertions and extra checks) */ -#cmakedefine P4EST_ENABLE_DEBUG @P4EST_ENABLE_DEBUG@ +#cmakedefine P4EST_ENABLE_DEBUG 1 -/* Undefine if: use aligned malloc (optionally use --enable-memalign=) */ -#cmakedefine P4EST_ENABLE_MEMALIGN +/* Define to 1 if we are using aligned malloc (optionally use --enable-memalign=) */ +#cmakedefine P4EST_ENABLE_MEMALIGN 1 /* Define to 1 if we are using MPI */ -#cmakedefine P4EST_ENABLE_MPI +#cmakedefine P4EST_ENABLE_MPI 1 /* Define to 1 if we can use MPI_COMM_TYPE_SHARED */ -#cmakedefine P4EST_ENABLE_MPICOMMSHARED +#cmakedefine P4EST_ENABLE_MPICOMMSHARED 1 /* Define to 1 if we are using MPI I/O */ -#cmakedefine P4EST_ENABLE_MPIIO +#cmakedefine P4EST_ENABLE_MPIIO 1 /* Define to 1 if we are using MPI_Init_thread */ -#cmakedefine P4EST_ENABLE_MPITHREAD +#cmakedefine P4EST_ENABLE_MPITHREAD 1 /* Define to 1 if we can use MPI_Win_allocate_shared */ -#cmakedefine P4EST_ENABLE_MPIWINSHARED +#cmakedefine P4EST_ENABLE_MPIWINSHARED 1 +/* Define to 1 if we can write vtk binary file data */ +#cmakedefine P4EST_ENABLE_VTK_BINARY 1 -/* Undefine if: write vtk ascii file data */ -#define P4EST_ENABLE_VTK_BINARY 1 +/* Define to 1 if we enable zlib compression for vtk binary data */ +#cmakedefine P4EST_ENABLE_VTK_COMPRESSION 1 -/* Undefine if: disable zlib compression for vtk binary data */ -#define P4EST_ENABLE_VTK_COMPRESSION 1 +/* Define to 1 if we use depreacted data file format */ +#cmakedefine P4EST_ENABLE_FILE_DEPRECATED 1 /* Define to a macro mangling the given C identifier (in lower and upper case), which must not contain underscores, for linking with Fortran. */ @@ -63,60 +65,59 @@ /* As FC_FUNC, but for C identifiers containing underscores. */ #define P4EST_FC_FUNC_(name,NAME) name ## _ +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_ARPA_INET_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_ARPA_INET_H +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_DLFCN_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_DLFCN_H +/* Define to 1 if we have the `fsync' function. */ +#cmakedefine P4EST_HAVE_FSYNC 1 -/* Define to 1 if you have the `fsync' function. */ -#cmakedefine P4EST_HAVE_FSYNC +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_INTTYPES_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_INTTYPES_H +/* Define to 1 if we found function pthread_create. */ +#cmakedefine HAVE_LPTHREAD 1 -/* Have we found function pthread_create. */ -#cmakedefine HAVE_LPTHREAD +/* Define to 1 if we found function lua_createtable. */ +#cmakedefine HAVE_LUA 1 -/* Have we found function lua_createtable. */ -#cmakedefine HAVE_LUA +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_MEMORY_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_MEMORY_H +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_NETINET_IN_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_NETINET_IN_H +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_WINSOCK2_H 1 -/* Define if you have the header file. */ -#cmakedefine P4EST_HAVE_WINSOCK2_H +/* Define to 1 if we have the `posix_memalign' function. */ +#cmakedefine P4EST_HAVE_POSIX_MEMALIGN 1 -/* Define to 1 if you have the `posix_memalign' function. */ -#cmakedefine P4EST_HAVE_POSIX_MEMALIGN +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_STDINT_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_STDINT_H +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_STDLIB_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_STDLIB_H +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_STRINGS_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_STRINGS_H +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_STRING_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_STRING_H +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_SYS_STAT_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_SYS_STAT_H +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_SYS_TYPES_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_SYS_TYPES_H +/* Define to 1 if we have the header file. */ +#cmakedefine P4EST_HAVE_UNISTD_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine P4EST_HAVE_UNISTD_H - -/* Have we found function adler32_combine. */ -#cmakedefine P4EST_HAVE_ZLIB +/* Define to 1 if we found function adler32_combine. */ +#cmakedefine P4EST_HAVE_ZLIB 1 /* Linker flags */ #define P4EST_LDFLAGS @P4EST_LDFLAGS@ @@ -124,18 +125,17 @@ /* Libraries */ #define P4EST_LIBS @P4EST_LIBS@ - /* Name of package */ #define P4EST_PACKAGE "p4est" /* Define to the address where bug reports for this package should be sent. */ -#define P4EST_PACKAGE_BUGREPORT "info@p4est.org" +#define P4EST_PACKAGE_BUGREPORT "p4est@ins.uni-bonn.de" /* Define to the full name of this package. */ #define P4EST_PACKAGE_NAME "p4est" /* Define to the full name and version of this package. */ -#define P4EST_PACKAGE_STRING "p4est 2.0.94-00da" +#define P4EST_PACKAGE_STRING "p4est @PROJECT_VERSION@" /* Define to the one symbol short name of this package. */ #define P4EST_PACKAGE_TARNAME "p4est" @@ -144,16 +144,16 @@ #define P4EST_PACKAGE_URL "" /* Define to the version of this package. */ -#define P4EST_PACKAGE_VERSION "2.0.94-00da" +#define P4EST_PACKAGE_VERSION "@PROJECT_VERSION@" /* Version number of package */ -#define P4EST_VERSION "2.0.94-00da" +#define P4EST_VERSION "@PROJECT_VERSION@" /* Package major version */ -#define P4EST_VERSION_MAJOR 2 +#define P4EST_VERSION_MAJOR @PROJECT_MAJOR@ /* Package minor version */ -#define P4EST_VERSION_MINOR 0 +#define P4EST_VERSION_MINOR @PROJECT_MINOR@ /* Package point version */ -#define P4EST_VERSION_POINT 94-00da +#define P4EST_VERSION_POINT @PROJECT_PATCH@ diff --git a/cmake/pkgconf.cmake b/cmake/pkgconf.cmake index e46aa1b54..590d7be51 100644 --- a/cmake/pkgconf.cmake +++ b/cmake/pkgconf.cmake @@ -8,4 +8,14 @@ set(pc_filename p4est-${git_version}.pc) configure_file(${CMAKE_CURRENT_LIST_DIR}/pkgconf.pc.in ${pc_filename} @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${pc_filename} - DESTINATION lib/pkgconfig) + DESTINATION lib/pkgconfig) + +set(pc_target ${pc_filename}) +set(pc_link ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig/p4est.pc) + +install(CODE "execute_process( \ + COMMAND ${CMAKE_COMMAND} -E create_symlink \ + ${pc_target} \ + ${pc_link} \ + )" + ) diff --git a/cmake/pkgconf.pc.in b/cmake/pkgconf.pc.in index 7ea555954..ba5c45952 100644 --- a/cmake/pkgconf.pc.in +++ b/cmake/pkgconf.pc.in @@ -7,6 +7,9 @@ includedir=${prefix}/include p4est_CC=@P4EST_CC@ p4est_CFLAGS=@P4EST_CPPFLAGS@ @P4EST_CFLAGS@ +p4est_enable_p6est=@enable_p6est@ +p4est_enable_p8est=@enable_p8est@ + Name: p4est Description: @CMAKE_PROJECT_DESCRIPTION@ Version: @git_version@ diff --git a/cmake/print_target_properties.cmake b/cmake/print_target_properties.cmake new file mode 100644 index 000000000..b61e0d2da --- /dev/null +++ b/cmake/print_target_properties.cmake @@ -0,0 +1,45 @@ +# this is a utility to explore all possible properties +# a given target may have +# see https://stackoverflow.com/questions/32183975/how-to-print-all-the-properties-of-a-target-in-cmake + +# +# Get all propreties that cmake supports +# +execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST) + +# +# Convert command output into a CMake list +# +STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") +STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") + +################################################################# +################################################################# +################################################################# +function(print_properties) + message ("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}") +endfunction(print_properties) + +################################################################# +################################################################# +################################################################# +function(print_target_properties tgt) + if(NOT TARGET ${tgt}) + message("There is no target named '${tgt}'") + return() + endif() + + foreach (prop ${CMAKE_PROPERTY_LIST}) + string(REPLACE "" "${CMAKE_BUILD_TYPE}" prop ${prop}) + # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i + if(prop STREQUAL "LOCATION" OR prop MATCHES "^LOCATION_" OR prop MATCHES "_LOCATION$") + continue() + endif() + # message ("Checking ${prop}") + get_property(propval TARGET ${tgt} PROPERTY ${prop} SET) + if (propval) + get_target_property(propval ${tgt} ${prop}) + message ("${tgt} ${prop} = ${propval}") + endif() + endforeach(prop) +endfunction(print_target_properties) diff --git a/cmake/sc.cmake b/cmake/sc.cmake deleted file mode 100644 index 4651c2cee..000000000 --- a/cmake/sc.cmake +++ /dev/null @@ -1,40 +0,0 @@ -# provides imported target SC::SC -include(ExternalProject) -include(${CMAKE_CURRENT_LIST_DIR}/GitSubmodule.cmake) - -set(sc_external true CACHE BOOL "build sc library" FORCE) - -git_submodule("${PROJECT_SOURCE_DIR}/sc") - -# --- libsc externalProject - -if(NOT SC_ROOT) - set(SC_ROOT ${CMAKE_INSTALL_PREFIX}) -endif() - -if(BUILD_SHARED_LIBS) - set(SC_LIBRARIES ${SC_ROOT}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}sc${CMAKE_SHARED_LIBRARY_SUFFIX}) -else() - set(SC_LIBRARIES ${SC_ROOT}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}sc${CMAKE_STATIC_LIBRARY_SUFFIX}) -endif() - -set(SC_INCLUDE_DIRS ${SC_ROOT}/include) - -ExternalProject_Add(SC -SOURCE_DIR ${PROJECT_SOURCE_DIR}/sc -CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${SC_ROOT} -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING:BOOL=false -Dmpi:BOOL=${mpi} -Dopenmp:BOOL=${openmp} -BUILD_BYPRODUCTS ${SC_LIBRARIES} -) - -# --- imported target - -file(MAKE_DIRECTORY ${SC_INCLUDE_DIRS}) -# avoid race condition - -# this GLOBAL is required to be visible via other -# project's FetchContent of this project -add_library(SC::SC INTERFACE IMPORTED GLOBAL) -target_include_directories(SC::SC INTERFACE "${SC_INCLUDE_DIRS}") -target_link_libraries(SC::SC INTERFACE "${SC_LIBRARIES}") - -add_dependencies(SC::SC SC) diff --git a/config/p4est_autotools.pc.in b/config/p4est_autotools.pc.in index bae6216b6..8d1e72634 100644 --- a/config/p4est_autotools.pc.in +++ b/config/p4est_autotools.pc.in @@ -12,7 +12,7 @@ Description: p4est manages a collection---a forest---of octrees in parallel. Version: @VERSION@ URL: https://www.p4est.org/ -Requires: libsc >= 2.8.2 +Requires: libsc >= 2.8.5 Cflags: -I@includedir@ Libs: -L@libdir@ -lp4est diff --git a/config/p4est_metis.m4 b/config/p4est_metis.m4 index d1d907368..0ee332d8a 100644 --- a/config/p4est_metis.m4 +++ b/config/p4est_metis.m4 @@ -4,7 +4,7 @@ dnl Check for the METIS library and link a test program dnl AC_DEFUN([P4EST_CHECK_METIS], [ -AC_MSG_CHECKING([for metis]) +AC_MSG_CHECKING([for METIS]) SC_ARG_WITH_PREFIX([metis], [enable metis-dependent code], [METIS], [$1]) if test "x$$1_WITH_METIS" != xno ; then @@ -32,7 +32,7 @@ if test "x$$1_WITH_METIS" != xno ; then &adjwgt, &nparts, &tpwgts, &ubvec, &options, &volume, &part); ]])],, - [AC_MSG_ERROR([Unable to link metis])]) + [AC_MSG_ERROR([unable to link METIS])]) dnl Keep the variables changed as done above dnl CPPFLAGS="$PRE_METIS_CPPFLAGS" dnl LDFLAGS="$PRE_METIS_LDFLAGS" diff --git a/config/p4est_petsc.m4 b/config/p4est_petsc.m4 index c107616d5..a9e949854 100644 --- a/config/p4est_petsc.m4 +++ b/config/p4est_petsc.m4 @@ -36,7 +36,7 @@ if test "x$$1_WITH_PETSC" != xno ; then fi fi if test ! -r $$1_PETSC_DIR/include/petscversion.h ; then - AC_MSG_ERROR([Unable to find readable petscversion.h]) + AC_MSG_ERROR([unable to find readable petscversion.h]) fi dnl see if we can determine the petsc arch string @@ -62,7 +62,7 @@ if test "x$$1_WITH_PETSC" != xno ; then make -s -C $$1_PETSC_DIR getincludedirs` elif test -r $$1_PETSC_DIR/conf/variables ; then if ! test -r $$1_PETSC_DIR/conf/rules ; then - AC_MSG_ERROR([Unable to find $$1_PETSC_DIR/makefile\ + AC_MSG_ERROR([unable to find $$1_PETSC_DIR/makefile\ or $$1_PETSC_DIR/conf/rules]) fi cat <Makefile_config_petsc @@ -78,7 +78,7 @@ EOF rm -f Makefile_config_petsc elif test -r $$1_PETSC_DIR/lib/petsc-conf/variables ; then if ! test -r $$1_PETSC_DIR/lib/petsc-conf/rules ; then - AC_MSG_ERROR([Unable to find $$1_PETSC_DIR/makefile or\ + AC_MSG_ERROR([unable to find $$1_PETSC_DIR/makefile or\ $$1_PETSC_DIR/lib/petsc-conf/rules]) fi cat <Makefile_config_petsc @@ -90,7 +90,7 @@ EOF rm -f Makefile_config_petsc elif test -r $$1_PETSC_DIR/lib/petsc/conf/variables ; then if ! test -r $$1_PETSC_DIR/lib/petsc/conf/rules ; then - AC_MSG_ERROR([Unable to find $$1_PETSC_DIR/makefile or\ + AC_MSG_ERROR([unable to find $$1_PETSC_DIR/makefile or\ $$1_PETSC_DIR/lib/petsc/conf/rules]) fi cat <Makefile_config_petsc @@ -102,7 +102,7 @@ EOF rm -f Makefile_config_petsc elif test -r $$1_PETSC_DIR/lib/petsc-conf/variables ; then if ! test -r $$1_PETSC_DIR/lib/petsc-conf/rules ; then - AC_MSG_ERROR([Unable to find $$1_PETSC_DIR/makefile or $$1_PETSC_DIR/lib/petsc-conf/rules]) + AC_MSG_ERROR([unable to find $$1_PETSC_DIR/makefile or $$1_PETSC_DIR/lib/petsc-conf/rules]) fi cat <Makefile_config_petsc include $$1_PETSC_DIR/lib/petsc-conf/variables @@ -112,7 +112,7 @@ EOF $1_PETSC_INCLUDE_DIRS=`make -s -f Makefile_config_petsc getincludedirs` rm -f Makefile_config_petsc else - AC_MSG_ERROR([Unable to find $$1_PETSC_DIR/makefile or $$1_PETSC_DIR/conf/variables\ + AC_MSG_ERROR([unable to find $$1_PETSC_DIR/makefile or $$1_PETSC_DIR/conf/variables\ or $$1_PETSC_DIR/lib/petsc-conf/variables or $$1_PETSC_DIR/lib/petsc/conf/variables]) fi PRE_PETSC_CPPFLAGS="$CPPFLAGS" @@ -128,7 +128,7 @@ EOF ierr = PetscFinalize(); return 0; ]])],, - [AC_MSG_ERROR([Unable to link petsc])]) + [AC_MSG_ERROR([unable to link petsc])]) CPPFLAGS="$PRE_PETSC_CPPFLAGS" LIBS="$PRE_PETSC_LIBS" diff --git a/config/p4est_soversion.in b/config/p4est_soversion.in new file mode 100644 index 000000000..b7f0269da --- /dev/null +++ b/config/p4est_soversion.in @@ -0,0 +1,7 @@ +# This file contains p4est's .so version in libtool format; see: +# https://www.gnu.org/software/libtool/manual/libtool.html#Versioning +# The format is P4EST_SOVERSION = current:revision:age (note the colons) + +# This file is included by src/Makefile.am and parsed by cmake/config.cmake. + +P4EST_SOVERSION = 3:0:0 diff --git a/configure.ac b/configure.ac index be49ad2dc..64c6b35e6 100644 --- a/configure.ac +++ b/configure.ac @@ -14,7 +14,7 @@ AC_PREFIX_DEFAULT([$PWD/local]) AX_PREFIX_CONFIG_H([config/p4est_config.h]) AM_INIT_AUTOMAKE([parallel-tests subdir-objects]) AM_SILENT_RULES -AM_EXTRA_RECURSIVE_TARGETS([justlibs]) +AM_EXTRA_RECURSIVE_TARGETS([justlibs doxygen]) PKG_INSTALLDIR SC_VERSION([P4EST]) @@ -26,6 +26,8 @@ P4EST_ARG_ENABLE([debug], [enable debug mode (assertions and extra checks)], [DEBUG]) P4EST_ARG_ENABLE([vtk-doubles], [use doubles for vtk file data], [VTK_DOUBLES]) +P4EST_ARG_ENABLE([file-deprecated], [use depreacted data file format], + [FILE_DEPRECATED]) P4EST_ARG_DISABLE([vtk-binary], [write vtk ascii file data], [VTK_BINARY]) P4EST_ARG_DISABLE([vtk-zlib], [disable zlib compression for vtk binary data], @@ -43,8 +45,6 @@ SC_MPI_CONFIG([P4EST], [], []) SC_MPI_ENGAGE([P4EST]) # This is needed for compatibility with automake >= 1.12 m4_ifdef([AM_PROG_AR],[AM_PROG_AR]) -dnl SC_PROG_LINT -dnl SC_C_VERSION LT_INIT echo "o---------------------------------------" @@ -98,7 +98,7 @@ echo "| TESTS: $P4EST_MPIRUN $P4EST_MPI_TEST_FLAGS" echo "o----------------------------------" # Create output files. -AC_CONFIG_FILES([Makefile Doxyfile]) +AC_CONFIG_FILES([Makefile doc/Doxyfile]) AC_CONFIG_FILES([config/Makefile.p4est.pre config/p4est_autotools.pc]) AC_OUTPUT diff --git a/Doxyfile.in b/doc/Doxyfile.in similarity index 74% rename from Doxyfile.in rename to doc/Doxyfile.in index 7abfcc98e..e30e1ecc4 100644 --- a/Doxyfile.in +++ b/doc/Doxyfile.in @@ -1,4 +1,4 @@ -# Doxyfile 1.8.6 +# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,11 +17,11 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -44,12 +44,13 @@ PROJECT_NUMBER = @VERSION@ # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = \ +"p4est is a software library for parallel adaptive mesh refinement." -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. PROJECT_LOGO = @@ -58,9 +59,9 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = @top_builddir@/doxygen +OUTPUT_DIRECTORY = @top_builddir@/doc -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where @@ -70,6 +71,14 @@ OUTPUT_DIRECTORY = @top_builddir@/doxygen CREATE_SUBDIRS = NO +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. @@ -85,14 +94,22 @@ CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -127,7 +144,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -171,6 +188,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -191,15 +218,23 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO @@ -218,16 +253,15 @@ TAB_SIZE = 8 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -256,25 +290,40 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # -# Note For files without extension you can use no_extension as a placeholder. +# Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -282,10 +331,19 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES @@ -307,7 +365,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -325,12 +383,19 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. -DISTRIBUTE_GROUP_DOC = YES +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that @@ -386,11 +451,24 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# 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 +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -400,35 +478,41 @@ LOOKUP_CACHE_SIZE = 0 EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. -EXTRACT_STATIC = YES +EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are +# included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. @@ -443,6 +527,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -453,21 +544,21 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these +# documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -480,22 +571,36 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the +# their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -523,14 +628,14 @@ INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that +# name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. @@ -553,7 +658,7 @@ SORT_MEMBERS_CTORS_1ST = NO # appear in their defined order. # The default value is: NO. -SORT_GROUP_NAMES = NO +SORT_GROUP_NAMES = YES # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will @@ -575,27 +680,25 @@ SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. @@ -620,8 +723,8 @@ ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES @@ -666,11 +769,10 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. +# search path. See also \cite for info how to create references. CITE_BIB_FILES = @@ -683,10 +785,10 @@ CITE_BIB_FILES = # messages are off. # The default value is: NO. -QUIET = YES +QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -694,7 +796,7 @@ QUIET = YES WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. @@ -711,12 +813,22 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -740,35 +852,41 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @top_srcdir@/src \ - @top_srcdir@/doc/mainpage.dox \ - @top_srcdir@/example/steps \ - @top_srcdir@/sc/src/sc.h \ - @top_srcdir@/sc/src/sc_containers.h \ - @top_srcdir@/example/p6est + @top_srcdir@/doc/doxygen # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. -FILE_PATTERNS = +FILE_PATTERNS = *.h \ + *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -817,14 +935,15 @@ EXCLUDE_SYMBOLS = SC_EXTERN_C_BEGIN \ # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = +EXAMPLE_PATH = @top_srcdir@/example/ # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = *.c \ + *.h # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands @@ -837,7 +956,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = @top_srcdir@/doc/images # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -853,6 +972,10 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -862,11 +985,15 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for +# INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. @@ -914,7 +1041,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -926,7 +1053,7 @@ REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. @@ -946,12 +1073,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -973,6 +1100,44 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -984,29 +1149,22 @@ VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 3 - # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -IGNORE_PREFIX = p2est_ \ - p4est_ \ - p6est_ \ - p8est_ +IGNORE_PREFIX = p2est_ P2EST_ \ + p4est_ P4EST_ \ + p6est_ P6EST_ \ + p8est_ P8EST_ #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1054,7 +1212,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = @top_srcdir@/doc/doxygen_footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1068,13 +1226,15 @@ HTML_FOOTER = HTML_STYLESHEET = -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1090,9 +1250,9 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to +# will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1121,12 +1281,24 @@ HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1150,13 +1322,14 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1195,8 +1368,8 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1218,28 +1391,29 @@ GENERATE_HTMLHELP = NO CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1270,7 +1444,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1278,8 +1453,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1287,30 +1462,30 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1352,7 +1527,7 @@ DISABLE_INDEX = NO # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has @@ -1371,7 +1546,7 @@ GENERATE_TREEVIEW = NO # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. -ENUM_VALUES_PER_LINE = 4 +ENUM_VALUES_PER_LINE = 1 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. @@ -1380,13 +1555,24 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1396,7 +1582,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1407,9 +1593,15 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. @@ -1420,7 +1612,7 @@ USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. @@ -1435,8 +1627,8 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1450,7 +1642,8 @@ MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1478,12 +1671,12 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There -# are two flavours of web server based searching depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. See -# the section "External Indexing and Searching" for details. +# implemented using a web server instead of a web client using JavaScript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. # The default value is: NO. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1495,9 +1688,10 @@ SERVER_BASED_SEARCH = NO # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1508,10 +1702,11 @@ EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1546,7 +1741,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = YES @@ -1562,22 +1757,36 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1595,9 +1804,12 @@ COMPACT_LATEX = NO PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1611,23 +1823,36 @@ EXTRA_PACKAGES = # # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will -# replace them by respectively the title of the page, the current date and time, -# only the current date, the version number of doxygen, the project name (see -# PROJECT_NAME), or the project number (see PROJECT_NUMBER). +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. # # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or @@ -1645,9 +1870,11 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1681,17 +1908,33 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1706,7 +1949,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1726,9 +1969,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1737,21 +1980,31 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for # classes and files. # The default value is: NO. -GENERATE_MAN = NO +GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1771,6 +2024,13 @@ MAN_OUTPUT = man MAN_EXTENSION = .3 +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without @@ -1784,7 +2044,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -1798,19 +2058,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify a XML DTD, which can be used by a -# validating XML parser to check the syntax of the XML files. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -1819,11 +2067,18 @@ XML_DTD = XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -1837,14 +2092,23 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen -# Definitions (see http://autogen.sf.net) file that captures the structure of -# the code including all documentation. Note that this feature is still -# experimental and incomplete at the moment. +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1853,7 +2117,7 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -1861,7 +2125,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -1869,9 +2133,9 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO the +# understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. @@ -1891,20 +2155,20 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names -# in the source code. If set to NO only conditional compilation will be +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then # the macro expansion is limited to the macros specified with the PREDEFINED and @@ -1912,9 +2176,9 @@ MACRO_EXPANSION = NO # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_ONLY_PREDEF = NO +EXPAND_ONLY_PREDEF = YES -# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1944,7 +2208,8 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = @PACKAGE_PREFIX@_DOXYGEN +PREDEFINED = @PACKAGE_PREFIX@_DOXYGEN __attribute__(x)= \ + P4EST_WITH_METIS # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -1956,9 +2221,9 @@ PREDEFINED = @PACKAGE_PREFIX@_DOXYGEN EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will -# remove all refrences to function-like macros that are alone on a line, have an -# all uppercase name, and do not end with a semicolon. Such function macros are -# typically used for boiler-plate code, and will confuse the parser if not +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not # removed. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -1978,7 +2243,7 @@ SKIP_FUNCTION_MACROS = YES # where loc1 and loc2 can be relative or absolute paths or URLs. See the # section "Linking to external documentation" for more information about the use # of tag files. -# Note: Each tag file must have an unique name (where the name does NOT include +# Note: Each tag file must have a unique name (where the name does NOT include # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. @@ -1988,39 +2253,34 @@ TAGFILES = # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. -GENERATE_TAGFILE = @top_builddir@/doxygen/@PACKAGE_NAME@.doxygen.tags +GENERATE_TAGFILE = @top_builddir@/doc/@PACKAGE_NAME@.doxygen.tags -# If the ALLEXTERNALS tag is set to YES all external class will be listed in the -# class index. If set to NO only the inherited external classes will be listed. +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. # The default value is: NO. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in -# the modules index. If set to NO, only the current project's groups will be +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. EXTERNAL_GROUPS = YES -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram # (in HTML and LaTeX) for classes with base or super classes. Setting the tag to # NO turns the diagrams off. Note that this option also works with HAVE_DOT # disabled, but it is recommended to install and use dot, since it yields more @@ -2029,15 +2289,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2045,7 +2296,7 @@ MSCGEN_PATH = DIA_PATH = -# If set to YES, the inheritance and collaboration graphs will hide inheritance +# If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2056,9 +2307,9 @@ HIDE_UNDOC_RELATIONS = YES # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: NO. +# The default value is: YES. -HAVE_DOT = NO +HAVE_DOT = @P4EST_HAVE_DOT@ # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2070,7 +2321,7 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font n the dot files that doxygen +# When you want a differently looking font in the dot files that doxygen # generates you can specify the font name using DOT_FONTNAME. You need to make # sure dot is able to find the font, which can be done by putting it in a # standard location or by setting the DOTFONTPATH environment variable or by @@ -2118,7 +2369,7 @@ COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2135,10 +2386,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2170,7 +2443,8 @@ INCLUDED_BY_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2181,7 +2455,8 @@ CALL_GRAPH = NO # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2204,11 +2479,17 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif and svg. +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2251,6 +2532,24 @@ MSCFILE_DIRS = DIAFILE_DIRS = +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized @@ -2287,7 +2586,7 @@ MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. @@ -2304,9 +2603,11 @@ DOT_MULTI_TARGETS = YES GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc and +# plantuml temporary files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/doc/FREEBSD b/doc/FREEBSD index 9c76845a7..33dd11f22 100644 --- a/doc/FREEBSD +++ b/doc/FREEBSD @@ -1,5 +1,4 @@ -Copyright (c) , -All rights reserved. +Copyright (c) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/doc/attic/p4est-build-wzlib.sh b/doc/attic/p4est-build-wzlib.sh new file mode 100755 index 000000000..3622ab91b --- /dev/null +++ b/doc/attic/p4est-build-wzlib.sh @@ -0,0 +1,55 @@ +#! /bin/bash + +# Build and install the latest p4est develop branch including zlib. +# We first download and install a recent zlib to a local directory, +# then clone and install the current develop branch of libsc using +# that zlib, and then clone and install the current develop branch +# of p4est using that libsc and zlib. + +# This results in three installation directories that any higher +# level software package may be compiled and linked against. +# The options are similar to those used in this script. + +# set installation root to local subdirectory +PREFIX="$PWD/local" + +# feel free to make changes to the libsc and p4est configure line. +CONFIG="--enable-mpi" + +# download, build and install zlib +wget -N https://www.zlib.net/zlib-1.3.tar.gz +tar -xvzf zlib-1.3.tar.gz +cd zlib-1.3 +./configure --prefix="$PREFIX/zlib" +make -j install +cd .. +rm -r zlib-1.3 zlib-1.3.tar.gz + +# provide environment that links to installed zlib +export CPPFLAGS="-I$PREFIX/zlib/include" +export CFLAGS="-O2 -g -Wall -Wl,-rpath=$PREFIX/zlib/lib" +export LDFLAGS="-L$PREFIX/zlib/lib" + +# clone, build and install libsc +git clone https://github.com/cburstedde/libsc.git -b develop +cd libsc +./bootstrap +mkdir build +cd build +../configure $CONFIG --prefix="$PREFIX/libsc" +make -j install V=0 +cd ../../ +rm -rf libsc/.git +rm -r libsc + +# clone, build and install p4est +git clone https://github.com/cburstedde/p4est.git -b develop +cd p4est +./bootstrap "$PREFIX/libsc/share/aclocal" +mkdir build +cd build +../configure $CONFIG --with-sc="$PREFIX/libsc" --prefix="$PREFIX/p4est" +make -j install V=0 +cd ../../ +rm -rf p4est/.git +rm -r p4est diff --git a/doc/author_aiton.txt b/doc/author_aiton.txt new file mode 100644 index 000000000..6f96f9b17 --- /dev/null +++ b/doc/author_aiton.txt @@ -0,0 +1 @@ +I place my contributions to p4est under FreeBSD license. Scott Aiton (scott@aiton.net) diff --git a/doc/author_bangerth.txt b/doc/author_bangerth.txt new file mode 100644 index 000000000..25b6fc58e --- /dev/null +++ b/doc/author_bangerth.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Wolfgang Bangerth (bangerth@colostate.edu) diff --git a/doc/author_brandt.txt b/doc/author_brandt.txt new file mode 100644 index 000000000..19e090384 --- /dev/null +++ b/doc/author_brandt.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Hannes Brandt (brandt@ins.uni-bonn.de) diff --git a/doc/author_carlin.txt b/doc/author_carlin.txt new file mode 100644 index 000000000..c243b9089 --- /dev/null +++ b/doc/author_carlin.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Louis Carlin (s33lcarl@uni-bonn.de) diff --git a/doc/author_fikl.txt b/doc/author_fikl.txt new file mode 100644 index 000000000..660383592 --- /dev/null +++ b/doc/author_fikl.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Alexandru Fikl (alexfikl@gmail.com) diff --git a/doc/author_frank.txt b/doc/author_frank.txt new file mode 100644 index 000000000..990b046a5 --- /dev/null +++ b/doc/author_frank.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Hannes Frank (derhannesfrank@web.de) diff --git a/doc/author_heister.txt b/doc/author_heister.txt new file mode 100644 index 000000000..2d1e4f6b4 --- /dev/null +++ b/doc/author_heister.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Timo Heister (heister@clemson.edu) diff --git a/doc/author_hereth.txt b/doc/author_hereth.txt new file mode 100644 index 000000000..74a89fef4 --- /dev/null +++ b/doc/author_hereth.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Ethan Alan Hereth (e.a.hereth@gmail.com) diff --git a/doc/author_hirsch.txt b/doc/author_hirsch.txt new file mode 100644 index 000000000..8aa6398f3 --- /dev/null +++ b/doc/author_hirsch.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license - Michael Hirsch. diff --git a/doc/author_hongli.txt b/doc/author_hongli.txt new file mode 100644 index 000000000..f8e88eb70 --- /dev/null +++ b/doc/author_hongli.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Hongli Lin (lin@ins.uni-bonn.de) diff --git a/doc/author_lahnert.txt b/doc/author_lahnert.txt index ea4baffa8..b4a1ff06b 100644 --- a/doc/author_lahnert.txt +++ b/doc/author_lahnert.txt @@ -1 +1 @@ -I place my contributions to p4est under the FreeBSD license. Michael Lahnert (michael.lahnert@ipvs.uni-stuttgart.de) +I place my contributions to p4est under the FreeBSD license. Michael Lahnert (michael.lahnert@gmail.com) diff --git a/doc/author_markert.txt b/doc/author_markert.txt new file mode 100644 index 000000000..4963b3f63 --- /dev/null +++ b/doc/author_markert.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Johannes Markert (johannes.markert@dlr.de) diff --git a/doc/author_prj-.txt b/doc/author_prj-.txt new file mode 100644 index 000000000..cb1ac1bef --- /dev/null +++ b/doc/author_prj-.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. prj- (https://github.com/prj-) diff --git a/doc/author_rudi.txt b/doc/author_rudi.txt new file mode 100644 index 000000000..460f14cb7 --- /dev/null +++ b/doc/author_rudi.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Johann Rudi (jrudi@vt.edu) diff --git a/doc/author_seastream.txt b/doc/author_seastream.txt new file mode 100644 index 000000000..157b426cc --- /dev/null +++ b/doc/author_seastream.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the FreeBSD license. Grant Seastream diff --git a/doc/author_turcksin.txt b/doc/author_turcksin.txt new file mode 100644 index 000000000..7e99124a4 --- /dev/null +++ b/doc/author_turcksin.txt @@ -0,0 +1,2 @@ +I place my contributions to p4est under FreeBSD license. +Bruno Turcksin (turcksinbr@ornl.gov) diff --git a/doc/author_wilcox.txt b/doc/author_wilcox.txt new file mode 100644 index 000000000..c49fec0ee --- /dev/null +++ b/doc/author_wilcox.txt @@ -0,0 +1 @@ +I place my contributions to p4est under the CC0 license. Lucas C Wilcox (lucas@swirlee.com) diff --git a/doc/doxygen.css b/doc/doxygen.css deleted file mode 100644 index 23a5e80fd..000000000 --- a/doc/doxygen.css +++ /dev/null @@ -1,1164 +0,0 @@ -/* The standard CSS for doxygen */ - -body, table, div, p, dl { -/* font: 400 14px/19px Roboto,sans-serif; */ - font: 400 14px/19px; -} - -/* @group Heading Levels */ - -h1 { - font-size: 150%; -} - -.title { - font-size: 150%; - font-weight: bold; - margin: 10px 2px; -} - -h2 { - border-bottom: 1px solid #87CBA9; - color: #357B58; - font-size: 150%; - font-weight: normal; - margin-top: 1.75em; - padding-top: 8px; - padding-bottom: 4px; - width: 100%; -} - -h3 { - font-size: 100%; -} - -h1, h2, h3, h4, h5, h6 { - -webkit-transition: text-shadow 0.5s linear; - -moz-transition: text-shadow 0.5s linear; - -ms-transition: text-shadow 0.5s linear; - -o-transition: text-shadow 0.5s linear; - transition: text-shadow 0.5s linear; - margin-right: 15px; -} - -h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { - text-shadow: 0 0 15px cyan; -} - -dt { - font-weight: bold; -} - -div.multicol { - -moz-column-gap: 1em; - -webkit-column-gap: 1em; - -moz-column-count: 3; - -webkit-column-count: 3; -} - -p.startli, p.startdd, p.starttd { - margin-top: 2px; -} - -p.endli { - margin-bottom: 0px; -} - -p.enddd { - margin-bottom: 4px; -} - -p.endtd { - margin-bottom: 2px; -} - -/* @end */ - -caption { - font-weight: bold; -} - -span.legend { - font-size: 70%; - text-align: center; -} - -h3.version { - font-size: 90%; - text-align: center; -} - -div.qindex, div.navtab{ - background-color: #EBF6F1; - border: 1px solid #A3D7BD; - text-align: center; -} - -div.qindex, div.navpath { - width: 100%; - line-height: 140%; -} - -div.navtab { - margin-right: 15px; -} - -/* @group Link Styling */ - -a { - color: #3D8C64; - font-weight: normal; - text-decoration: none; -} - -.contents a:visited { - color: #46A274; -} - -a:hover { - text-decoration: underline; -} - -a.qindex { - font-weight: bold; -} - -a.qindexHL { - font-weight: bold; - background-color: #9CD4B8; - color: #ffffff; - border: 1px double #86CAA8; -} - -.contents a.qindexHL:visited { - color: #ffffff; -} - -a.el { - font-weight: bold; -} - -a.elRef { -} - -a.code, a.code:visited { - color: #4665A2; -} - -a.codeRef, a.codeRef:visited { - color: #4665A2; -} - -/* @end */ - -dl.el { - margin-left: -1cm; -} - -pre.fragment { - border: 1px solid #C4CFE5; - background-color: #FBFCFD; - padding: 4px 6px; - margin: 4px 8px 4px 2px; - overflow: auto; - word-wrap: break-word; - font-size: 9pt; - line-height: 125%; - font-family: monospace, fixed; - font-size: 105%; -} - -div.fragment { - padding: 4px; - margin: 4px; - background-color: #FBFDFC; - border: 1px solid #C4E5D5; -} - -div.line { - font-family: monospace, fixed; - font-size: 13px; - min-height: 13px; - line-height: 1.0; - text-wrap: unrestricted; - white-space: -moz-pre-wrap; /* Moz */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - white-space: pre-wrap; /* CSS3 */ - word-wrap: break-word; /* IE 5.5+ */ - text-indent: -53px; - padding-left: 53px; - padding-bottom: 0px; - margin: 0px; - -webkit-transition-property: background-color, box-shadow; - -webkit-transition-duration: 0.5s; - -moz-transition-property: background-color, box-shadow; - -moz-transition-duration: 0.5s; - -ms-transition-property: background-color, box-shadow; - -ms-transition-duration: 0.5s; - -o-transition-property: background-color, box-shadow; - -o-transition-duration: 0.5s; - transition-property: background-color, box-shadow; - transition-duration: 0.5s; -} - -div.line.glow { - background-color: cyan; - box-shadow: 0 0 10px cyan; -} - - -span.lineno { - padding-right: 4px; - text-align: right; - border-right: 2px solid #0F0; - background-color: #E8E8E8; - white-space: pre; -} -span.lineno a { - background-color: #D8D8D8; -} - -span.lineno a:hover { - background-color: #C8C8C8; -} - -div.ah { - background-color: black; - font-weight: bold; - color: #ffffff; - margin-bottom: 3px; - margin-top: 3px; - padding: 0.2em; - border: solid thin #333; - border-radius: 0.5em; - -webkit-border-radius: .5em; - -moz-border-radius: .5em; - box-shadow: 2px 2px 3px #999; - -webkit-box-shadow: 2px 2px 3px #999; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); - background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); -} - -div.groupHeader { - margin-left: 16px; - margin-top: 12px; - font-weight: bold; -} - -div.groupText { - margin-left: 16px; - font-style: italic; -} - -body { - background-color: white; - color: black; - margin: 0; -} - -div.contents { - margin-top: 10px; - margin-left: 12px; - margin-right: 8px; -} - -td.indexkey { - background-color: #EBF6F1; - font-weight: bold; - border: 1px solid #C4E5D5; - margin: 2px 0px 2px 0; - padding: 2px 10px; - white-space: nowrap; - vertical-align: top; -} - -td.indexvalue { - background-color: #EBF6F1; - border: 1px solid #C4E5D5; - padding: 2px 10px; - margin: 2px 0px; -} - -tr.memlist { - background-color: #EEF7F2; -} - -p.formulaDsp { - text-align: center; -} - -img.formulaDsp { - -} - -img.formulaInl { - vertical-align: middle; -} - -div.center { - text-align: center; - margin-top: 0px; - margin-bottom: 0px; - padding: 0px; -} - -div.center img { - border: 0px; -} - -address.footer { - text-align: right; - padding-right: 12px; -} - -img.footer { - border: 0px; - vertical-align: middle; -} - -/* @group Code Colorization */ - -span.keyword { - color: #008000 -} - -span.keywordtype { - color: #604020 -} - -span.keywordflow { - color: #e08000 -} - -span.comment { - color: #800000 -} - -span.preprocessor { - color: #806020 -} - -span.stringliteral { - color: #002080 -} - -span.charliteral { - color: #008080 -} - -span.vhdldigit { - color: #ff00ff -} - -span.vhdlchar { - color: #000000 -} - -span.vhdlkeyword { - color: #700070 -} - -span.vhdllogic { - color: #ff0000 -} - -blockquote { - background-color: #F7FBF9; - border-left: 2px solid #9CD4B8; - margin: 0 24px 0 4px; - padding: 0 12px 0 16px; -} - -/* @end */ - -/* -.search { - color: #003399; - font-weight: bold; -} - -form.search { - margin-bottom: 0px; - margin-top: 0px; -} - -input.search { - font-size: 75%; - color: #000080; - font-weight: normal; - background-color: #e8eef2; -} -*/ - -td.tiny { - font-size: 75%; -} - -.dirtab { - padding: 4px; - border-collapse: collapse; - border: 1px solid #A3D7BD; -} - -th.dirtab { - background: #EBF6F1; - font-weight: bold; -} - -hr { - height: 0px; - border: none; - border-top: 1px solid #4AAA7A; -} - -hr.footer { - height: 1px; -} - -/* @group Member Descriptions */ - -table.memberdecls { - border-spacing: 0px; - padding: 0px; -} - -.memberdecls td, .fieldtable tr { - -webkit-transition-property: background-color, box-shadow; - -webkit-transition-duration: 0.5s; - -moz-transition-property: background-color, box-shadow; - -moz-transition-duration: 0.5s; - -ms-transition-property: background-color, box-shadow; - -ms-transition-duration: 0.5s; - -o-transition-property: background-color, box-shadow; - -o-transition-duration: 0.5s; - transition-property: background-color, box-shadow; - transition-duration: 0.5s; -} - -.memberdecls td.glow, .fieldtable tr.glow { - background-color: cyan; - box-shadow: 0 0 15px cyan; -} - -.mdescLeft, .mdescRight, -.memItemLeft, .memItemRight, -.memTemplItemLeft, .memTemplItemRight, .memTemplParams { - background-color: #F9FCFA; - border: none; - margin: 4px; - padding: 1px 0 0 8px; -} - -.mdescLeft, .mdescRight { - padding: 0px 8px 4px 8px; - color: #555; -} - -.memItemLeft, .memItemRight, .memTemplParams { - border-bottom: 1px solid #DEF0E7; -} - -.memItemLeft, .memTemplItemLeft { - white-space: nowrap; -} - -.memItemRight { - width: 100%; -} - -.memTemplParams { - color: #46A274; - white-space: nowrap; -} - -/* @end */ - -/* @group Member Details */ - -/* Styles for detailed member documentation */ - -.memtemplate { - font-size: 80%; - color: #46A274; - font-weight: normal; - margin-left: 9px; -} - -.memnav { - background-color: #EBF6F1; - border: 1px solid #A3D7BD; - text-align: center; - margin: 2px; - margin-right: 15px; - padding: 2px; -} - -.mempage { - width: 100%; -} - -.memitem { - padding: 0; - margin-bottom: 10px; - margin-right: 5px; - -webkit-transition: box-shadow 0.5s linear; - -moz-transition: box-shadow 0.5s linear; - -ms-transition: box-shadow 0.5s linear; - -o-transition: box-shadow 0.5s linear; - transition: box-shadow 0.5s linear; - display: table !important; - width: 100%; -} - -.memitem.glow { - box-shadow: 0 0 15px cyan; -} - -.memname { - font-weight: bold; - margin-left: 6px; -} - -.memname td { - vertical-align: bottom; -} - -.memproto, dl.reflist dt { - border-top: 1px solid #A8D9C0; - border-left: 1px solid #A8D9C0; - border-right: 1px solid #A8D9C0; - padding: 6px 0px 6px 0px; - color: #25553D; - font-weight: bold; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - background-image:url('nav_f.png'); - background-repeat:repeat-x; - background-color: #E2F2EA; - /* opera specific markup */ - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - border-top-right-radius: 4px; - border-top-left-radius: 4px; - /* firefox specific markup */ - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - -moz-border-radius-topright: 4px; - -moz-border-radius-topleft: 4px; - /* webkit specific markup */ - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - -webkit-border-top-right-radius: 4px; - -webkit-border-top-left-radius: 4px; - -} - -.memdoc, dl.reflist dd { - border-bottom: 1px solid #A8D9C0; - border-left: 1px solid #A8D9C0; - border-right: 1px solid #A8D9C0; - padding: 6px 10px 2px 10px; - background-color: #FBFDFC; - border-top-width: 0; - background-image:url('nav_g.png'); - background-repeat:repeat-x; - background-color: #FFFFFF; - /* opera specific markup */ - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - /* firefox specific markup */ - -moz-border-radius-bottomleft: 4px; - -moz-border-radius-bottomright: 4px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - /* webkit specific markup */ - -webkit-border-bottom-left-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); -} - -dl.reflist dt { - padding: 5px; -} - -dl.reflist dd { - margin: 0px 0px 10px 0px; - padding: 5px; -} - -.paramkey { - text-align: right; -} - -.paramtype { - white-space: nowrap; -} - -.paramname { - color: #602020; - white-space: nowrap; -} -.paramname em { - font-style: normal; -} -.paramname code { - line-height: 14px; -} - -.params, .retval, .exception, .tparams { - margin-left: 0px; - padding-left: 0px; -} - -.params .paramname, .retval .paramname { - font-weight: bold; - vertical-align: top; -} - -.params .paramtype { - font-style: italic; - vertical-align: top; -} - -.params .paramdir { - font-family: "courier new",courier,monospace; - vertical-align: top; -} - -table.mlabels { - border-spacing: 0px; -} - -td.mlabels-left { - width: 100%; - padding: 0px; -} - -td.mlabels-right { - vertical-align: bottom; - padding: 0px; - white-space: nowrap; -} - -span.mlabels { - margin-left: 8px; -} - -span.mlabel { - background-color: #72C19A; - border-top:1px solid #53B484; - border-left:1px solid #53B484; - border-right:1px solid #C4E5D5; - border-bottom:1px solid #C4E5D5; - text-shadow: none; - color: white; - margin-right: 4px; - padding: 2px 3px; - border-radius: 3px; - font-size: 7pt; - white-space: nowrap; -} - - - -/* @end */ - -/* these are for tree view when not used as main index */ - -div.directory { - margin: 10px 0px; - border-top: 1px solid #A8B8D9; - border-bottom: 1px solid #A8B8D9; - width: 100%; -} - -.directory table { - border-collapse:collapse; -} - -.directory td { - margin: 0px; - padding: 0px; - vertical-align: top; -} - -.directory td.entry { - white-space: nowrap; - padding-right: 6px; -} - -.directory td.entry a { - outline:none; -} - -.directory td.entry a img { - border: none; -} - -.directory td.desc { - width: 100%; - padding-left: 6px; - padding-right: 6px; - padding-top: 3px; - border-left: 1px solid rgba(0,0,0,0.05); -} - -.directory tr.even { - padding-left: 6px; - background-color: #F7FBF9; -} - -.directory img { - vertical-align: -30%; -} - -.directory .levels { - white-space: nowrap; - width: 100%; - text-align: right; - font-size: 9pt; -} - -.directory .levels span { - cursor: pointer; - padding-left: 2px; - padding-right: 2px; - color: #3D8C64; -} - -div.dynheader { - margin-top: 8px; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -address { - font-style: normal; - color: #2A6146; -} - -table.doxtable { - border-collapse:collapse; - margin-top: 4px; - margin-bottom: 4px; -} - -table.doxtable td, table.doxtable th { - border: 1px solid #2D684A; - padding: 3px 7px 2px; -} - -table.doxtable th { - background-color: #377F5B; - color: #FFFFFF; - font-size: 110%; - padding-bottom: 4px; - padding-top: 5px; -} - -table.fieldtable { - width: 100%; - margin-bottom: 10px; - border: 1px solid #A8D9C0; - border-spacing: 0px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); - box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); -} - -.fieldtable td, .fieldtable th { - padding: 3px 7px 2px; -} - -.fieldtable td.fieldtype, .fieldtable td.fieldname { - white-space: nowrap; - border-right: 1px solid #A8D9C0; - border-bottom: 1px solid #A8D9C0; - vertical-align: top; -} - -.fieldtable td.fielddoc { - border-bottom: 1px solid #A8D9C0; - width: 100%; -} - -.fieldtable tr:last-child td { - border-bottom: none; -} - -.fieldtable th { - background-image:url('nav_f.png'); - background-repeat:repeat-x; - background-color: #E2F2EA; - font-size: 90%; - color: #25553D; - padding-bottom: 4px; - padding-top: 5px; - text-align:left; - -moz-border-radius-topleft: 4px; - -moz-border-radius-topright: 4px; - -webkit-border-top-left-radius: 4px; - -webkit-border-top-right-radius: 4px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom: 1px solid #A8D9C0; -} - - -.tabsearch { - top: 0px; - left: 10px; - height: 36px; - background-image: url('tab_b.png'); - z-index: 101; - overflow: hidden; - font-size: 13px; -} - -.navpath ul -{ - font-size: 11px; - background-image:url('tab_b.png'); - background-repeat:repeat-x; - height:30px; - line-height:30px; - color:#8ACCAB; - border:solid 1px #C2E4D3; - overflow:hidden; - margin:0px; - padding:0px; -} - -.navpath li -{ - list-style-type:none; - float:left; - padding-left:10px; - padding-right:15px; - background-image:url('bc_s.png'); - background-repeat:no-repeat; - background-position:right; - color:#367C59; -} - -.navpath li.navelem a -{ - height:32px; - display:block; - text-decoration: none; - outline: none; - font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; -} - -.navpath li.navelem a:hover -{ - color:#68BD92; -} - -.navpath li.footer -{ - list-style-type:none; - float:right; - padding-left:10px; - padding-right:15px; - background-image:none; - background-repeat:no-repeat; - background-position:right; - color:#367C59; - font-size: 8pt; -} - - -div.summary -{ - float: right; - font-size: 8pt; - padding-right: 5px; - width: 50%; - text-align: right; -} - -div.summary a -{ - white-space: nowrap; -} - -div.ingroups -{ - font-size: 8pt; - width: 50%; - text-align: left; -} - -div.ingroups a -{ - white-space: nowrap; -} - -div.header -{ - background-image:url('nav_h.png'); - background-repeat:repeat-x; - background-color: #F9FCFA; - margin: 0px; - border-bottom: 1px solid #C4E5D5; -} - -div.headertitle -{ - padding: 5px 5px 5px 10px; -} - -dl -{ - padding: 0 0 0 10px; -} - -/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ -dl.section -{ - margin-left: 0px; - padding-left: 0px; -} - -dl.note -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #D0C000; -} - -dl.warning, dl.attention -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #FF0000; -} - -dl.pre, dl.post, dl.invariant -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #00D000; -} - -dl.deprecated -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #505050; -} - -dl.todo -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #00C0E0; -} - -dl.test -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #3030E0; -} - -dl.bug -{ - margin-left:-7px; - padding-left: 3px; - border-left:4px solid; - border-color: #C08050; -} - -dl.section dd { - margin-bottom: 6px; -} - - -#projectlogo -{ - text-align: center; - vertical-align: bottom; - border-collapse: separate; -} - -#projectlogo img -{ - border: 0px none; -} - -#projectname -{ - font: 300% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 2px 0px; -} - -#projectbrief -{ - font: 120% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; -} - -#projectnumber -{ - font: 50% Tahoma, Arial,sans-serif; - margin: 0px; - padding: 0px; -} - -#titlearea -{ - padding: 0px; - margin: 0px; - width: 100%; - border-bottom: 1px solid #53B484; -} - -.image -{ - text-align: center; -} - -.dotgraph -{ - text-align: center; -} - -.mscgraph -{ - text-align: center; -} - -.caption -{ - font-weight: bold; -} - -div.zoom -{ - border: 1px solid #90CEAF; -} - -dl.citelist { - margin-bottom:50px; -} - -dl.citelist dt { - color:#337554; - float:left; - font-weight:bold; - margin-right:10px; - padding:5px; -} - -dl.citelist dd { - margin:2px 0; - padding:5px 0; -} - -div.toc { - padding: 14px 25px; - background-color: #F4FAF7; - border: 1px solid #D8EEE3; - border-radius: 7px 7px 7px 7px; - float: right; - height: auto; - margin: 0 20px 10px 10px; - width: 200px; -} - -div.toc li { - background: url("bdwn.png") no-repeat scroll 0 5px transparent; - font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; - margin-top: 5px; - padding-left: 10px; - padding-top: 2px; -} - -div.toc h3 { - font: bold 12px/1.2 Arial,FreeSans,sans-serif; - color: #46A274; - border-bottom: 0 none; - margin: 0; -} - -div.toc ul { - list-style: none outside none; - border: medium none; - padding: 0px; -} - -div.toc li.level1 { - margin-left: 0px; -} - -div.toc li.level2 { - margin-left: 15px; -} - -div.toc li.level3 { - margin-left: 30px; -} - -div.toc li.level4 { - margin-left: 45px; -} - -.inherit_header { - font-weight: bold; - color: gray; - cursor: pointer; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.inherit_header td { - padding: 6px 0px 2px 5px; -} - -.inherit { - display: none; -} - -tr.heading h2 { - margin-top: 12px; - margin-bottom: 4px; -} - -@media print -{ - #top { display: none; } - #side-nav { display: none; } - #nav-path { display: none; } - body { overflow:visible; } - h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } - .summary { display: none; } - .memitem { page-break-inside: avoid; } - #doc-content - { - margin-left:0 !important; - height:auto !important; - width:auto !important; - overflow:inherit; - display:inline; - } -} - diff --git a/doc/doxygen/example_points.dox b/doc/doxygen/example_points.dox new file mode 100644 index 000000000..07e974585 --- /dev/null +++ b/doc/doxygen/example_points.dox @@ -0,0 +1,78 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \page example_points Documentation for the point example program + * + * The points example refines a domain according to a given set of points. + */ + +/** \example points/generate_points2.c + * + * Auxiliary program to generate a file of points in parallel. + * It uses the MPI I/O functionality of libsc to create one large file. + * The file is written to in parallel and using partitioned file access. + * + * The file contains first a binary integer \ref p4est_gloidx_t + * storing the global number of points and then the list of point + * coordinates as 3-tuples of binary type double. + * + * The usage of the program is + * + * p4est_points_generate + * + * where configuration is one of + * + * - `unit` The 2D unit square, + * - `brick` A 2x3 grid of squares, + * - `three` Three squares meeting at a non-planary angle, + * - `moebius` A five-square moebius strip embedded in 3D space, + * - `star` A star composed of six rhomboids, + * - `periodic` The all-periodic unit square + * + * and prefix is an output basename or filename to which we append `.pts`. + */ + +/** \example points/generate_points3.c + * + * Auxiliary program to generate a file of points in parallel. + * It uses the MPI I/O functionality of libsc to create one large file. + * The file is written to in parallel and using partitioned file access. + * + * The file contains first a binary integer \ref p4est_gloidx_t + * storing the global number of points and then the list of point + * coordinates as 3-tuples of binary type double. + * + * The usage of the program is + * + * p8est_points_generate + * + * where configuration is one of + * + * - `unit` The unit cube, + * - `brick` An example brick connectivity using configuration + * (2,3,4) as the number of trees per direction. + * - `periodic` The unit cube with all-periodic boundary conditions. + * - `rotwrap` The unit cube with various self-periodic b.c. + * - `twocubes` Two connected cubes. + * - `rotcubes` A collection of six connected rotated cubes. + */ diff --git a/doc/doxygen/example_simple.dox b/doc/doxygen/example_simple.dox new file mode 100644 index 000000000..006c8c7ba --- /dev/null +++ b/doc/doxygen/example_simple.dox @@ -0,0 +1,272 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \page example_simple2 Documentation for selected 2D example programs + * + * The p4est library comes with various example programs. + * They are kept under the subdirectory + * [example](https://github.com/cburstedde/p4est/tree/master/example). + * Most have both a 2D and a 3D version. + * When the library is configured `--enable-mpi`, they can all be run in + * parallel on any number of MPI ranks, even on small computers. + * + * One first helpful program to try out is called `p4est_simple` (2D version) + * and `p8est_simple` (3D version), both under + * [simple](https://github.com/cburstedde/p4est/tree/master/example/simple). + * We showcase some 2D results further below on this page, and we encourage + * everyone to play with the command line arguments. + * + * ### The first step example + * + * Quite some time later, we created a range of step-by-step examples under + * [steps](https://github.com/cburstedde/p4est/tree/master/example/steps). + * Let us begin here with the first one that generates a mesh spelling + * 'Hello, World!': \ref steps/p4est_step1.c. + * + * This program performs refinement on a simple domain based on hardcoded + * image data. + * As a result, the output VTK file displays the phrase 'Hello World' by the + * mesh refinement. + * + * Usage may be one of: + * + * > `p4est_step1` + * or with MPI: + * > `mpirun -np 3 p4est_step1` + * + * * No. of trees: 1 + * * Maximum refinement level: 6 + * + * \image html HW.png + * + * ### The historic simple example + * + * Another illustrative example can be found in \ref simple/simple2.c. + * The refinement pattern is generated by some hardcoded prescriptions based + * on a quadrant's tree number, refinement level and coordinates. + * Please see the documentation under that link for a full list of configurations. + * + * This program creates/refines & coarsens/balances/partitions one + * of several available geometries specified on the command line. + * As a result, the output VTK files document all + * the steps of the mesh manipulation process. + * + * * Example: periodic + * + * Create a connectivity structure for a periodic unit square. + * The left and right faces are identified, and bottom and top opposite. + * + * Usage: + * > `p4est_simple periodic 5` + * or with MPI: + * > `mpirun -np 4 p4est_simple periodic 5` + * + * * No. of trees: 1 + * * Maximum refinement level: chosen on the command line as 5 + * + * \image html periodic_balanced_lv5.png + * + * * Example: circle + * + * Create a connectivity structure for an donut-like circle. + * The circle consists of 6 trees connecting each other by their faces. + * The trees are laid out as a hexagon between \f$[-2, 2]\f$ in the y direction + * and \f$[-\sqrt{3}, \sqrt{3}]\f$ in the x direction. The hexagon has flat + * sides along the y direction and pointy ends in x. + * + * Usage: + * > `p4est_simple circle 5` + * or with MPI: + * > `mpirun -np 4 p4est_simple circle 5` + * + * * No. of trees: 6 + * * Maximum refinement level: chosen on the command line as 5 + * + * \image html circle_balanced_lv5.png + * + * * Example: drop + * + * Create a connectivity structure for a five-trees geometry with a hole. + * The geometry covers the square \f$[0, 3]^2\f$, where the hole is \f$[1, 2]^2\f$. + * + * Usage: + * > `p4est_simple drop 5` + * or with MPI: + * > `mpirun -np 7 p4est_simple drop 5` + * + * * No. of trees: 5 + * * Maximum refinement level: chosen on the command line as 5 + * \image html drop_balanced_lv5.png + * + * * Example: moebius + * + * Create a connectivity structure for a five-tree moebius band. + * + * Usage: + * > `p4est_simple moebius 5` + * or with MPI: + * > `mpirun -np 4 p4est_simple moebius 5` + * + * * No. of trees: 5 + * * Maximum refinement level: chosen on the command line as 5 + * + * \image html moebius_balanced_lv5.png + */ + + /** \page example_simple3 Documentation for selected 3D example programs + * + * The p4est library comes with various example programs. + * They are kept under the subdirectory + * [example](https://github.com/cburstedde/p4est/tree/master/example). + * Most have both a 2D and a 3D version. + * When the library is configured `--enable-mpi`, they can all be run in + * parallel on any number of MPI ranks, even on small computers. + * + * Before proceeding with the examples on this page, it is highly + * recommended to get familiar with the programs, starting from 2D + * examples in \ref example_simple2. + * + * One first helpful program to try out is called `p4est_simple` (2D version) + * and `p8est_simple` (3D version), both under + * [simple](https://github.com/cburstedde/p4est/tree/master/example/simple). + * On this page, we showcase some results of 3D example programs further + * below on this page, and we encourage everyone to play with the command + * line arguments. + * + * ### The historic simple example + * + * The example can be found in \ref simple/simple3.c. It is a 3D version of + * \ref simple/simple2.c + * The refinement pattern is generated by some hardcoded prescriptions based + * on a octants's tree number, refinement level and coordinates. + * Please see the documentation under the link for a full list of configurations. + * + * This program creates/refines & coarsens/balances/partitions one + * of several available geometries specified on the command line. + * As a result, the output VTK files document all + * the steps of the mesh manipulation process. + * + * * Example: unit + * + * Create a connectivity structure for a stand-alone unit cube. + * + * Usage: + * > `p8est_simple unit 5` + * or with MPI: + * > `mpirun -np 4 p8est_simple unit 5` + * + * * No. of trees: 1 + * * Maximum refinement level: chosen on the command line as 5 + * + * \image html unit_balanced_lv5.png + * + * * Example: periodic + * + * Further enriching the upper example, this example program creates a + * connectivity structure of a unit cube with all-periodic boundary conditions. + * + * Usage: + * > `p8est_simple periodic 5` + * or with MPI: + * > `mpirun -np 4 p8est_simple periodic 5` + * + * * No. of trees: 1 + * * Maximum refinement level: chosen on the command line as 5 + * + * \image html periodic_3d_balanced_lv5.png + * + * * Example: two wraps + * + * Further developing the ideas above, this example program creates a + * connectivity structure of two wrap-up cubes (each of which is a tree). The + * visually not connected far ends of the two cubes are periodically identical. + * + * Usage: + * > `p8est_simple twowraps 5` + * or with MPI: + * > `mpirun -np 7 p8est_simple twowraps 5` + * + * * No. of trees: 2 + * * Maximum refinement level: chosen on the command line as 5 + * \image html twowraps_balanced_lv5.png + * + */ + +/** \example steps/p4est_step1.c + * This software refines a basic domain using given image data. + * As a result, the resulting VTK file showcases the message 'Hello + * World' through the mesh. + * + * Usage: + * > `p4est_step1` + */ + +/** \example simple/simple2.c + * This application generates, refines, and adjusts a mesh set on the command line. + * The resulting VTK files visually present each stage of the mesh + * modification procedure. + * + * Usage: + * > `p4est_simple ` + * possible configurations: + * * unit Refinement on the unit square. + * * brick Refinement on a regular forest of octrees. + * * three Refinement on a forest with three trees. + * * evil Check second round of refinement with np=5 level=7 + * * evil3 Check second round of refinement on three trees + * * pillow Refinement on a 2-tree pillow-shaped domain. + * * moebius Refinement on a 5-tree Moebius band. + * * star Refinement on a 6-tree star shaped domain. + * * cubed Refinement on a 6-tree cubed sphere surface. + * * disk Refinement on a 5-tree spherical standard disk. + * * xdisk Refinement on a 5-tree spherical disk periodic in x. + * * ydisk Refinement on a 5-tree spherical disk periodic in y. + * * pdisk Refinement on a 5-tree spherical disk, periodic b.c. + * * periodic Refinement on the unit square with all-periodic b.c. + * * rotwrap Refinement on the unit square with weird periodic b.c. + * * circle Refinement on a 6-tree donut-like circle. + * * drop Refinement on a 5-trees geometry with an inner hole. + * * icosahedron Refinement on the sphere + * * shell2d Refinement on a 2d shell with geometry. + * * disk2d Refinement on a 2d disk with geometry. + * * bowtie Refinement on a 2-tree bowtie domain. + */ + +/** \example simple/simple3.c + * This application generates, refines, and adjusts a mesh set on the command line. + * The resulting VTK files visually present each stage of the mesh + * modification procedure. + * + * Usage: + * > `p8est_simple ` + * possible configurations: + * * unit The unit cube. + * * brick The brick connectivity. + * * periodic The unit cube with all-periodic boundary conditions. + * * rotwrap The unit cube with various self-periodic b.c. + * * twocubes Two connected cubes. + * * twowrap Two cubes with periodically identified far ends. + * * rotcubes A collection of six connected rotated cubes. + * * shell A 24-tree discretization of a hollow sphere. + * * sphere A 13-tree discretization of a solid sphere. + */ diff --git a/doc/doxygen/installation.dox b/doc/doxygen/installation.dox new file mode 100644 index 000000000..2ff328d9c --- /dev/null +++ b/doc/doxygen/installation.dox @@ -0,0 +1,198 @@ +/** + * \page installing_p4est Installation + * + * We collect helpful hints to build the p4est software library and examples. + * + * \tableofcontents + * + * \section debian Using a Debian package + * Installing `p4est` on Debian-based systems via a package manager is the most + * straightforward method to get started. This method is beneficial for users who + * prefer a managed installation and do not require the latest version or + * customization of the build process. This provides a frozen and stable version + * of the code with minimal hassle. However, contributions back to the software + * aren't possible in this manner. + * + * Before proceeding, ensure your package lists are up to date. + * Open a terminal and update your system's package list with the following command: + * + * > `sudo apt-get update` + * + * Once the package lists are updated, you can install `p4est` using `apt-get`. + * + * > `sudo apt-get install libp4est-` + * + * The available version can be found in the + * [debian package tracker](https://packages.qa.debian.org/p/p4est.html), + * or by running + * + * > `apt-cache search p4est` + * + * \section tarbuild Installing p4est from a .tar.gz archive + * + * No special tools are needed to build from a tarball obtained for example from + * [the release directory](https://github.com/p4est/p4est.github.io/tree/master/release/). + * + * After unpacking the tar file, we recommend to change into an empty build directory. + * The `configure` script from the toplevel directory of the unpacked tar file + * can be executed as is. + * We recommend to create an empty build directory. + * + * \subsection steps1 Step-by-step guide + * + * \subsubsection unpack_tarball 1. Unpack the tarball + * + * > `tar -xzf p4est-.tar.gz` + * + * \subsubsection create_dir 2. Create and change into a build directory + * + * > `mkdir p4est_build` + * + * > `cd p4est_build` + * + * \subsubsection run_script 3. Run the configure script + * + * > `../p4est-/configure --help` + * + * > `../p4est-/configure --enable-mpi --etc...` + * + * You may also set environment variables like `CFLAGS="-Wall -g"`. + * + * \subsubsection complile_software 4. Compile the software + * + * > `make -j V=0` + * + * The environment variable `V=0` suppresses most of `make`'s console output. + * + * \subsubsection install_software 5. Install the software (optional) + * + * > `make install V=0` + * + * The software is installed by default into the prefix `./local`. + * This can be changed by setting `configure --prefix=`. + * + * \section install Building from source on Unix-like systems + * + * For a more hands-on approach, consider cloning the + * p4est GitHub repository. You'll need familiarity with the revision control + * tool `git` or its GUI frontends. The branch `develop` is recommended + * as it has minimized dependencies and houses the latest algorithms. To + * generate the configure script, call the `./bootstrap` script in the p4est + * top-level source directory. This requires a working installation of GNU + * autoconf, automake, and libtool. + * + * \subsection prerequisites Prerequisites + * - For the installation from source, we still prefer the autotools + * over the CMake system (which has recently been added). + * Ensure you have: + * - \c automake + * - \c autoconf + * - \c libtool + * - \c make + * + * \subsection optional Optional components + * - For enhancing parallel processing, having \c MPI is advisable + * and configuring `--enable-mpi`. + * + * \subsection steps2 Step-by-step guide + * + * \subsubsection source_retrieval 1. Source retrieval + * + * > `git clone https://github.com/cburstedde/p4est.git` + * + * \subsubsection bootstrap 2. Bootstrap execution + * + * Navigate to the \c p4est directory and initiate the bootstrap script, + * especially if you've cloned it from GitHub: + * + * > `cd p4est` + * + * > `./bootstrap` + * + * \subsubsection setup 3. Setting up p4est + * + * Decide on a compilation directory. For this guide, `~/p4est_build` is used, + * presuming the source is in `~/p4est`: + * + * > `mkdir ~/p4est_build` + * + * > `cd ~/p4est_build` + * + * > `../p4est/configure [OPTIONS]` + * + * The \c configure script is quite versatile and has multiple options, among + * them: + * + * - `--enable-debug`: Activates debugging mode, increasing verbosity and turns + * on `P4EST_ASSERT` for checks. + * - `--enable-mpi`: This integrates the `mpi.h` header and triggers MPI compiler + * wrappers. Without this option, MPI routine wrappers are employed for serial + * compilation. + * - `--disable-mpiio`: This avoids using `MPI_File` based calls. + * + * Additionally, the command: + * + * > `../p4est/configure --help` + * + * prints a list of currently available options to configure. + * + * A common developer-oriented configuration might resemble: + * + * > `relative/path/to/configure CFLAGS="-Wall -Wuninitialized -O0 -g" --enable-mpi --enable-debug` + * + * While a production-oriented configuration could be: + * + * > `relative/path/to/configure CFLAGS="-Wall -Wno-unused-but-set-variable -O2" --enable-mpi` + * + * \subsubsection compile 4. Compiling p4est + * + * After configuration, the next step is the compilation of \c p4est: + * + * > `make -j V=0` + * + * > `make install -j V=0` + * + * \subsubsection verification 5. Verification Step + * + * It's recommended to execute the \c p4est test programs post-installation + * to ensure everything is in order: + * + * > `make -j4 check V=0` + * + * Where the `-j` option limits parallel make to at most four targets at the same time. + * + * \subsubsection api_docs 6. Autogenerated API documentation + * + * The Doxygen output for p4est and libsc can be recreated with the following + * command after the configuration process: + * + * > `make doxygen` + * + * This requires the doxygen program and produces html, latex and man format + * documentation under `doc`. + * + * \subsubsection integration 7. Integrating with p4est + * + * For projects aiming to incorporate \c p4est as an external library + * + * > `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/p4est_install/lib` + * + * to be able to run built executables that dynamically link to p4est. + * Append to the compile command: + * + * > `-I$HOME/p4est_install/include -L$HOME/p4est_install/lib -lp4est -lsc -lz -lm` + * + * For full functionality, we expect a zlib that provides the function + * `adler32_combine`. + * + * \section further Further information and links + * + * You may find further documentation for installation on both + * [Linux](https://github.com/cburstedde/p4est/blob/develop/INSTALL) and + * [Windows](https://github.com/cburstedde/p4est/blob/develop/INSTALL_WINDOWS) + * in the source folder. + * + * For users with Windows 10 version 2004 and higher, you may also try + * [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) as p4est works + * well in the subsystem. + */ diff --git a/doc/mainpage.dox b/doc/doxygen/mainpage.dox similarity index 98% rename from doc/mainpage.dox rename to doc/doxygen/mainpage.dox index f88c88de9..0bd04528c 100644 --- a/doc/mainpage.dox +++ b/doc/doxygen/mainpage.dox @@ -52,6 +52,7 @@ * [source](http://github.com/cburstedde/p4est) under the * [doc/](https://github.com/cburstedde/p4est/tree/master/doc) directory. * + * We provide installation instructions under \ref installing_p4est. * To build the p4est library from a tar distribution, use the standard * procedure of the GNU autotools. The configure script takes the following * options: diff --git a/doc/doxygen_footer.html b/doc/doxygen_footer.html new file mode 100644 index 000000000..a90759cbc --- /dev/null +++ b/doc/doxygen_footer.html @@ -0,0 +1,5 @@ + + + + + diff --git a/doc/images/HW.png b/doc/images/HW.png new file mode 100644 index 000000000..2ea94fda4 Binary files /dev/null and b/doc/images/HW.png differ diff --git a/doc/images/circle_balanced_lv5.png b/doc/images/circle_balanced_lv5.png new file mode 100644 index 000000000..016f8b519 Binary files /dev/null and b/doc/images/circle_balanced_lv5.png differ diff --git a/doc/images/circle_nodes.png b/doc/images/circle_nodes.png new file mode 100644 index 000000000..c86389cbe Binary files /dev/null and b/doc/images/circle_nodes.png differ diff --git a/doc/images/drop_balanced_lv5.png b/doc/images/drop_balanced_lv5.png new file mode 100644 index 000000000..7b5d70a6e Binary files /dev/null and b/doc/images/drop_balanced_lv5.png differ diff --git a/doc/images/moebius_balanced_lv5.png b/doc/images/moebius_balanced_lv5.png new file mode 100644 index 000000000..0a60e848c Binary files /dev/null and b/doc/images/moebius_balanced_lv5.png differ diff --git a/doc/images/periodic_3d_balanced_lv5.png b/doc/images/periodic_3d_balanced_lv5.png new file mode 100644 index 000000000..bdc834b28 Binary files /dev/null and b/doc/images/periodic_3d_balanced_lv5.png differ diff --git a/doc/images/periodic_balanced_lv5.png b/doc/images/periodic_balanced_lv5.png new file mode 100644 index 000000000..f77f44ca4 Binary files /dev/null and b/doc/images/periodic_balanced_lv5.png differ diff --git a/doc/images/twowraps_balanced_lv5.png b/doc/images/twowraps_balanced_lv5.png new file mode 100644 index 000000000..fce078d81 Binary files /dev/null and b/doc/images/twowraps_balanced_lv5.png differ diff --git a/doc/images/unit_balanced_lv5.png b/doc/images/unit_balanced_lv5.png new file mode 100644 index 000000000..a17b25c2a Binary files /dev/null and b/doc/images/unit_balanced_lv5.png differ diff --git a/doc/morton/morton.py b/doc/morton/morton.py index b8f3f60f1..5f35005c2 100755 --- a/doc/morton/morton.py +++ b/doc/morton/morton.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 import os,sys,argparse diff --git a/doc/p4est-build-wdeps.sh b/doc/p4est-build-wdeps.sh new file mode 100755 index 000000000..9d8440bb5 --- /dev/null +++ b/doc/p4est-build-wdeps.sh @@ -0,0 +1,31 @@ +#! /bin/bash + +# Build and install the latest p4est develop branch including zlib and +# jansson. We first download and install a recent zlib to a local +# directory, then do the same for the jansson library. Then we clone and +# install the current develop branch of libsc using that zlib and jansson +# installation and finally clone and install the current develop branch of +# p4est, linking against all of the above libraries. + +# This results in four installation directories that any higher +# level software package may be compiled and linked against. +# The options are similar to those used in this script. +# In particular, the -rpath option may turn out useful. + +# let the libsc installation script set the stage +BLSC="libsc-build-wdeps.sh" +wget -N "https://github.com/cburstedde/libsc/raw/develop/doc/$BLSC" && \ +source "$BLSC" && \ +rm "$BLSC" || exit 1 + +# clone, build and install p4est +git clone --depth 1 https://github.com/cburstedde/p4est.git -b develop && \ +cd p4est && \ +./bootstrap "$PREFIX/libsc/share/aclocal" && \ +mkdir build && \ +cd build && \ +../configure $CONFIG --with-sc="$PREFIX/libsc" --prefix="$PREFIX/p4est" && \ +make -j install V=0 && \ +cd ../../ && \ +rm -rf p4est/.git && \ +rm -r p4est || bdie "p4est" diff --git a/doc/patch/patch-valgrind-CI.patch b/doc/patch/patch-valgrind-CI.patch new file mode 100644 index 000000000..de98161b9 --- /dev/null +++ b/doc/patch/patch-valgrind-CI.patch @@ -0,0 +1,35 @@ +From 519fc99a2bd926fe37cfd5b10b0916e379190e16 Mon Sep 17 00:00:00 2001 +From: Tim Griesbach +Date: Mon, 17 Oct 2022 18:03:10 +0200 +Subject: [PATCH] Patch valgrind CI + +Due to a possible bug in p8est_is_balanced we disable +p8est_test_balance for now. +--- + test/Makefile.am | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/test/Makefile.am b/test/Makefile.am +index df0f0a89..6453488c 100644 +--- a/test/Makefile.am ++++ b/test/Makefile.am +@@ -33,7 +33,7 @@ endif + endif + if P4EST_ENABLE_BUILD_3D + p4est_test_programs += \ +- test/p8est_test_quadrants test/p8est_test_balance \ ++ test/p8est_test_quadrants \ + test/p8est_test_partition test/p8est_test_coarsen \ + test/p8est_test_valid test/p8est_test_balance_type \ + test/p8est_test_face_transform test/p8est_test_edge_face_corners \ +@@ -116,7 +116,6 @@ endif + + if P4EST_ENABLE_BUILD_3D + test_p8est_test_quadrants_SOURCES = test/test_quadrants3.c +-test_p8est_test_balance_SOURCES = test/test_balance3.c + test_p8est_test_partition_SOURCES = test/test_partition3.c + test_p8est_test_coarsen_SOURCES = test/test_coarsen3.c + test_p8est_test_valid_SOURCES = test/test_valid3.c +-- +2.17.1 + diff --git a/doc/release_notes.txt b/doc/release_notes.txt new file mode 100644 index 000000000..cc5a1591a --- /dev/null +++ b/doc/release_notes.txt @@ -0,0 +1,54 @@ + +# Release notes for the p4est software library + +## Link to latest version DOI + +(This one is currently outdated. See below under version 2.8.6.) +[![DOI](https://zenodo.org/badge/3974666.svg)](https://zenodo.org/doi/10.5281/zenodo.10839050) + +## General + + - We have been reminded, for good reason, to maintain release notes. + - Please record explicitly if there have been any breaking changes wrt. the + software API or ABI. In this case, we'll bump the libtool version with + the next release version. Please also note if there have been A*I + extensions, in which case we'll bump the libtool minor version. + +## 2.8.6 + +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.10839051.svg)](https://doi.org/10.5281/zenodo.10839051) + +There have been breaking changes, very strictly speaking. + +### Build system + + - Bump libsc under subdirectory sc/ to v2.8.6. + - Bump libtool library version of p4est and libsc to 3:0:0. + - Enhance CMake build script in accordance with changes under sc/. + +### Documentation + + - Create a page of examples for users to get familiar with the software. + - Arrange all doxygen pages into a subdirectory doc/doxygen. + - Fix the warnings while generating documentation for p4est_connectivity.h. + - Fix the warnings while generating documentation for p8est_connectivity.h. + - Add more explicit documentation to the p?est_connectivity.h files. + - Fix the warnings while generating documentation for p4est_mesh.h. + - Fix the warnings while generating documentation for p8est_mesh.h. + - Add more explicit documentation to the p?est_mesh.h files. + +### Functionality + + - Add two connectivities (2d bowtie and 3d drop) to stress the balance demo. + - Add a new 3d connectivity (drop) into the simple example. + - Add three 2d connectivities (circle, drop and bowtie) into simple. + - Integrate updated MPI I/O wrappers and CMake logic in libsc. + - Reset quadrant data size to 0 in p{4,8}est_wrap_new_p4est. + - Add search_partition_gfp operating without a gfq array. + - Add a parameter struct for the p{4,8}est_wrap_t. + - Add a parameter struct for the p{4,8}est_mesh_t. + - Add an option to store edge-hanging corner neighbors in the mesh. + +## 2.8.5 + +This was the last version without a release notes file. diff --git a/doc/webpage/cite.html b/doc/webpage/cite.html index 027492910..0e7225ba9 100644 --- a/doc/webpage/cite.html +++ b/doc/webpage/cite.html @@ -35,8 +35,19 @@

Binary packages

Autogenerated API documentation

-This is (somewhat outdated) doxygen output for -p4est. +This is the doxygen output for p4est +and for libsc. Recreate it with make doxygen after calling configure.

@@ -122,7 +124,7 @@

Questions / Get involved

For further questions and suggestions, please email us at p4est@ins.uni-bonn.de. We have reconfigured this -email list, +email list, so if your subscription request did not go through the first time, please try again.

diff --git a/doc/webpage/tutorial-build.html b/doc/webpage/tutorial-build.html index 6d38a5a9c..5fe158958 100644 --- a/doc/webpage/tutorial-build.html +++ b/doc/webpage/tutorial-build.html @@ -92,7 +92,7 @@

Getting to the source

Alternatively, you may find the +href="https://p4est.github.io/release/p4est-2.8.tar.gz"> latest tarball online, unpack it, then run configure and make. If you specify an installation prefix to configure and run make install, you will find the libraries and example programs in their proper directory. diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 6bbd622e3..c59cc61a3 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,59 +1,15 @@ -cmake_minimum_required(VERSION 3.13...3.21) +cmake_minimum_required(VERSION 3.22) project(p4estExamples LANGUAGES C) -include(CTest) +enable_testing() + include(CheckSymbolExists) include(CheckIncludeFile) -option(mpi "use MPI" off) - -# --- find external libraries -list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake/Modules) - -if(mpi) - find_package(MPI COMPONENTS C REQUIRED) - if(NOT MPIEXEC_EXECUTABLE) - message(FATAL_ERROR "MPIEXEC not found") - endif() -endif(mpi) - -find_package(ZLIB REQUIRED) - -# --- find our library -find_package(SC REQUIRED) -find_package(P4EST REQUIRED) - -# --- get system capabilities - -check_symbol_exists(sqrt math.h P4EST_NONEED_M) -if(NOT P4EST_NONEED_M) - set(CMAKE_REQUIRED_LIBRARIES m) - check_symbol_exists(sqrt math.h P4EST_NEED_M) -endif() - -target_link_libraries(SC::SC INTERFACE -$<$:MPI::MPI_C> -$<$:ZLIB::ZLIB> -$<$:m> -) -# --- get system capabilities -check_include_file(arpa/inet.h P4EST_HAVE_ARPA_INET_H) -check_include_file(netinet/in.h P4EST_HAVE_NETINET_IN_H) -if(WIN32 AND NOT P4EST_HAVE_ARPA_INET_H AND NOT P4EST_HAVE_NETINET_IN_H) - check_include_file(Winsock2.h P4EST_HAVE_WINSOCK2_H) - target_link_libraries(SC::SC INTERFACE wsock32 ws2_32) # Iphlpapi -endif() - -if(NOT (P4EST_HAVE_ARPA_INET_H OR P4EST_HAVE_NETINET_IN_H OR P4EST_HAVE_WINSOCK2_H)) - message(FATAL_ERROR "A networking library was not found.") -endif() - -check_include_file(unistd.h P4EST_HAVE_UNISTD_H) -if(P4EST_HAVE_UNISTD_H) - check_include_file(getopt.h P4EST_HAVE_GETOPT_H) +if(PROJECT_IS_TOP_LEVEL) + find_package(P4EST CONFIG REQUIRED) endif() - cmake_host_system_information(RESULT Ncpu QUERY NUMBER_OF_PHYSICAL_CORES) if(Ncpu LESS 2) include(ProcessorCount) @@ -64,85 +20,141 @@ if(Ncpu LESS 2) endif() # --- helper functions +# it is not intended to run examples as tests in the top-level project +if(NOT PROJECT_IS_TOP_LEVEL) + return() +endif() + -function(p4est_example name files arg1 arg2) +function(p4est_example name files dir) add_executable(${name} ${files}) -target_link_libraries(${name} PRIVATE P4EST::P4EST SC::SC) -if(MPI_FOUND) - add_test(NAME p4est:${name} COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${Ncpu} $ ${arg1} ${arg2}) -else() - add_test(NAME p4est:${name} COMMAND $ ${arg1} ${arg2}) -endif() -set_tests_properties(p4est:${name} PROPERTIES - TIMEOUT 60 - RESOURCE_LOCK cpu_mpi) +target_link_libraries(${name} PRIVATE P4EST::P4EST) +set_target_properties(${name} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/example/${dir}" + LABELS p4est +) endfunction(p4est_example) -function(p8est_example name files arg1 arg2) +function(p8est_example name files dir) add_executable(${name} ${files}) -target_link_libraries(${name} PRIVATE P4EST::P4EST SC::SC) -if(MPI_FOUND) - add_test(NAME p8est:${name} COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${Ncpu} $ ${arg1} ${arg2}) -else() - add_test(NAME p8est:${name} COMMAND $ ${arg1} ${arg2}) -endif() -set_tests_properties(p8est:${name} PROPERTIES - TIMEOUT 60 - RESOURCE_LOCK cpu_mpi) +target_link_libraries(${name} PRIVATE P4EST::P4EST) +set_target_properties(${name} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/example/${dir}" + LABELS p8est +) endfunction(p8est_example) + +function(p4est_copy_resource dir res_file) +configure_file( + ${PROJECT_SOURCE_DIR}/${dir}/${res_file} + ${PROJECT_BINARY_DIR}/example/${dir}/${res_file} + COPYONLY +) +endfunction() + # --- setup examples -# p4est_example(mesh2 mesh/mesh2.c unit 4) # mesh/mesh2.c:291:9: error: too many arguments to function ‘p4est_mesh_quadrant_cumulative’ -# p4est_example(points2 points/points2.c unit 4) # missing .pts input files +p4est_example(points2 points/points2.c "point") +if(P4EST_ENABLE_P8EST) + p8est_example(points3 points/points3.c "point") +endif() -p4est_example(simple2 simple/simple2.c unit 4) -if(TARGET P4EST::P8EST) - p8est_example(simple3 simple/simple3.c unit 4) +p4est_example(generate_points2 points/generate_points2.c "point") +if(P4EST_ENABLE_P8EST) + p8est_example(generate_points3 points/generate_points3.c "point") endif() if(P4EST_HAVE_GETOPT_H) -foreach(n bricks timings) - p4est_example(${n}2 timings/${n}2.c "" "") - if(TARGET P4EST::P8EST) - p8est_example(${n}3 timings/${n}3.c "" "") +set(n particles) +p4est_example(${n}2 ${n}/${n}2.c ${n}) +if(P4EST_ENABLE_P8EST) + p8est_example(${n}3 ${n}/${n}3.c ${n}) +endif() +p4est_copy_resource(${n} separt.pl) + +set(n spheres) +p4est_example(${n}2 "${n}/${n}2.c;${n}/p4est_${n}.c" ${n}) +if(P4EST_ENABLE_P8EST) + p8est_example(${n}3 "${n}/${n}3.c;${n}/p8est_${n}.c" ${n}) +endif() + +foreach(n IN ITEMS bricks timings loadconn) + p4est_example(${n}2 timings/${n}2.c "timings") + if(P4EST_ENABLE_P8EST) + p8est_example(${n}3 timings/${n}3.c ${n} "timings") endif() endforeach() +foreach(n IN ITEMS timana tsrana) + foreach(r IN ITEMS awk sh) + p4est_copy_resource(timings ${n}.${r}) + endforeach() +endforeach() +p4est_copy_resource(timings perfscript.sh) -# optparse not recognizing -l --level -# p4est_example(loadconn2 timings/loadconn2.c "-l 4" "") +p8est_example(tsearch3 timings/tsearch3.c "timings") -set(n spheres) -p4est_example(${n}2 "${n}/${n}2.c;${n}/p4est_${n}.c" "" "") -if(TARGET P4EST::P8EST) - p8est_example(${n}3 "${n}/${n}3.c;${n}/p8est_${n}.c" "" "") -endif() +endif(P4EST_HAVE_GETOPT_H) -set(n particles) -p4est_example(${n}2 ${n}/${n}2.c "" "") -if(TARGET P4EST::P8EST) - p8est_example(${n}3 ${n}/${n}3.c "" "") +set(n balance_seeds) +p4est_example(${n}2 balance/${n}2.c "balance") +if(P4EST_ENABLE_P8EST) + p8est_example(${n}3 balance/${n}3.c "balance") endif() -endif(P4EST_HAVE_GETOPT_H) +foreach(n IN ITEMS mesh simple) + p4est_example(${n}2 ${n}/${n}2.c ${n}) + if(P4EST_ENABLE_P8EST) + p8est_example(${n}3 ${n}/${n}3.c ${n}) + endif() +endforeach() +p4est_copy_resource(mesh conndebug.p8c) +p4est_example(periodicity3 mesh/periodicity3.c "mesh") + +p4est_example(count_quadrants2 search/count_quadrants2.c "search") +if(P4EST_ENABLE_P8EST) + p8est_example(count_quadrants3 search/count_quadrants3.c "search") +endif() -foreach(i 1 3 4) +set(s steps) +foreach(i RANGE 1 5) set(n p4est_step${i}) - p4est_example(${n} steps/${n}.c "" "") + p4est_example(${n} ${s}/${n}.c ${s}) - if(TARGET P4EST::P8EST) + if(P4EST_ENABLE_P8EST) set(n p8est_step${i}) - p8est_example(${n} steps/${n}.c "" "") + p8est_example(${n} ${s}/${n}.c ${s}) endif() endforeach() -# optparse not recognizing -l --level -# p4est_example(p4est_step2 steps/p4est_step2.c ${CMAKE_CURRENT_SOURCE_DIR}/steps/hole_2d_cubit.inp 4) -# set_tests_properties(p4est:p4est_step2 PROPERTIES TIMEOUT 300) +foreach(n IN ITEMS cubit.inp cubit.jou gmsh.geo gmsh.inp) + p4est_copy_resource(${s} hole_2d_${n}) +endforeach() +if(P4EST_ENABLE_P8EST) + foreach(n IN ITEMS cubit.inp cubit.jou gmsh.geo gmsh.inp) + p4est_copy_resource(${s} hole_3d_${n}) + endforeach() + endif() + +set(t tetgen) +foreach(n IN ITEMS read_conn write_conn) + p4est_example(${n}2 ${t}/${n}2.c ${t}) + if(P4EST_ENABLE_P8EST) + p8est_example(${n}3 ${t}/${n}3.c ${t}) + endif() +endforeach() +if(P4EST_ENABLE_P8EST) + p8est_example(read_${t} ${t}/read_${t}.c ${t}) + foreach(r IN ITEMS ele node) + p4est_copy_resource(${t} p8est_box_${t}.${r}) + endforeach() +endif() diff --git a/example/balance/Makefile.am b/example/balance/Makefile.am new file mode 100644 index 000000000..8fe31bcf1 --- /dev/null +++ b/example/balance/Makefile.am @@ -0,0 +1,24 @@ + +# This file is part of p4est. +# Makefile.am in example/balance +# included non-recursively from toplevel directory + +if P4EST_ENABLE_BUILD_2D +bin_PROGRAMS += example/balance/p4est_balance_seeds +bin_PROGRAMS += example/balance/p4est_balance_corner + +example_balance_p4est_balance_seeds_SOURCES = \ + example/balance/balance_seeds2.c +example_balance_p4est_balance_corner_SOURCES = \ + example/balance/balance_corner2.c +endif + +if P4EST_ENABLE_BUILD_3D +bin_PROGRAMS += example/balance/p8est_balance_seeds +bin_PROGRAMS += example/balance/p8est_balance_corner + +example_balance_p8est_balance_seeds_SOURCES = \ + example/balance/balance_seeds3.c +example_balance_p8est_balance_corner_SOURCES = \ + example/balance/balance_corner3.c +endif diff --git a/example/balance/balance_corner2.c b/example/balance/balance_corner2.c new file mode 100644 index 000000000..f4c10c8ca --- /dev/null +++ b/example/balance/balance_corner2.c @@ -0,0 +1,142 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef P4_TO_P8 +#include +#include +#include +#else +#include +#include +#include +#endif + +typedef struct +{ + sc_MPI_Comm mpicomm; + int mpisize; + int mpirank; +} +mpi_context_t; + +/* refinement level initialization */ +static int refine_level = 0; + +/* refinement function */ +static int +refine_fn (p4est_t * p4est, p4est_topidx_t which_tree, + p4est_quadrant_t * quadrant) +{ + if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) { + return 0; + } + if (quadrant->level == 1 && p4est_quadrant_child_id (quadrant) == 3) { + return 1; + } + if (quadrant->x == P4EST_LAST_OFFSET (2) && + quadrant->y == P4EST_LAST_OFFSET (2)) { + return 1; + } + if (quadrant->x >= P4EST_QUADRANT_LEN (2)) { + return 0; + } + + return 1; +} + +int +main (int argc, char **argv) +{ + int mpiret; + int wrongusage; + const char *usage; + p4est_connectivity_t *connectivity; + mpi_context_t mpi_context, *mpi = &mpi_context; + p4est_geometry_t *geom; + p4est_t *p4est; + + /* initialize MPI and p4est internals */ + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + mpi->mpicomm = sc_MPI_COMM_WORLD; + mpiret = sc_MPI_Comm_size (mpi->mpicomm, &mpi->mpisize); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Comm_rank (mpi->mpicomm, &mpi->mpirank); + SC_CHECK_MPI (mpiret); + + sc_init (mpi->mpicomm, 1, 1, NULL, SC_LP_DEFAULT); + p4est_init (NULL, SC_LP_DEFAULT); + + /* usage error if the input is not in the correct format */ + usage = + "Arguments: \n" + " Level: controls the maximum depth of refinement\n"; + wrongusage = 0; + if (!wrongusage && argc != 2) { + wrongusage = 1; + } + if (wrongusage) { + P4EST_GLOBAL_LERROR (usage); + sc_abort_collective ("Usage error"); + } + + /* assign variables based on configuration */ + refine_level = atoi (argv[1]); + + /* create connectivity and forest structures */ + geom = NULL; +#ifndef P4_TO_P8 + /* this 2D connectivity is challenging for the balance algorithm */ + connectivity = p4est_connectivity_new_bowtie (); +#else + /* this 3D connectivity is challenging for the balance algorithm */ + connectivity = p8est_connectivity_new_drop (); +#endif + p4est = p4est_new_ext (mpi->mpicomm, connectivity, 0, 0, 1, 0, NULL, geom); + p4est_vtk_write_file (p4est, geom, P4EST_STRING "_corner_new"); + + /* refinement */ + p4est_refine (p4est, 1, refine_fn, NULL); + p4est_vtk_write_file (p4est, geom, P4EST_STRING "_corner_refine"); + + /* balance */ + p4est_balance (p4est, P4EST_CONNECT_FULL, NULL); + p4est_vtk_write_file (p4est, geom, P4EST_STRING "_corner_balance"); + + /* partition */ + p4est_partition (p4est, 0, NULL); + p4est_vtk_write_file (p4est, geom, P4EST_STRING "_corner_partition"); + + /* destroy p4est and its connectivity */ + p4est_destroy (p4est); + p4est_connectivity_destroy (connectivity); + + /* clean up and exit */ + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + + return 0; +} diff --git a/example/balance/balance_corner3.c b/example/balance/balance_corner3.c new file mode 100644 index 000000000..39a124db0 --- /dev/null +++ b/example/balance/balance_corner3.c @@ -0,0 +1,26 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include "balance_corner2.c" diff --git a/example/balance_seeds/balance_seeds2.c b/example/balance/balance_seeds2.c similarity index 92% rename from example/balance_seeds/balance_seeds2.c rename to example/balance/balance_seeds2.c index 9d35026cb..74c23857f 100644 --- a/example/balance_seeds/balance_seeds2.c +++ b/example/balance/balance_seeds2.c @@ -46,7 +46,7 @@ static p4est_quadrant_t center = { 0x10000000, 0x10000000, 2, 0, 0, {NULL} }; #else static const int refine_level = 8; static p4est_quadrant_t center = - { 0x20000, 0x20000, 0x20000, 2, 0, 0, {NULL} }; + { 0x10000000, 0x10000000, 0x10000000, 2, 0, 0, {NULL} }; #endif static void @@ -124,6 +124,7 @@ main (int argc, char **argv) p4est_tree_t *tree; sc_array_t *quadrants; size_t zz, count; + double *vtkvec; p4est_quadrant_t *q; int i; #ifndef P4_TO_P8 @@ -163,17 +164,20 @@ main (int argc, char **argv) context = p4est_vtk_write_header (context); SC_CHECK_ABORT (context != NULL, P4EST_STRING "_vtk: Error writing header"); + vtkvec = P4EST_ALLOC (double, p4est->local_num_quadrants * P4EST_CHILDREN); tree = p4est_tree_array_index (p4est->trees, 0); quadrants = &(tree->quadrants); count = quadrants->elem_count; - level = sc_array_new_count (sizeof (double), count * P4EST_CHILDREN); + P4EST_ASSERT (count <= (size_t) p4est->local_num_quadrants); for (zz = 0; zz < count; zz++) { q = p4est_quadrant_array_index (quadrants, zz); for (i = 0; i < P4EST_CHILDREN; i++) { - *(double *) sc_array_index (level, P4EST_CHILDREN * zz + i) = - (double) ((balance_seeds_elem_t *) (q->p.user_data))->flag; + vtkvec[P4EST_CHILDREN * zz + i] = (double) + ((balance_seeds_elem_t *) (q->p.user_data))->flag; } } + level = + sc_array_new_data (vtkvec, sizeof (double), count * P4EST_CHILDREN); context = p4est_vtk_write_point_dataf (context, 1, 0, "level", level, context); SC_CHECK_ABORT (context != NULL, @@ -183,6 +187,7 @@ main (int argc, char **argv) retval = p4est_vtk_write_footer (context); SC_CHECK_ABORT (!retval, P4EST_STRING "_vtk: Error writing footer"); + P4EST_FREE (vtkvec); p4est_destroy (p4est); p4est_connectivity_destroy (connectivity); diff --git a/example/balance_seeds/balance_seeds3.c b/example/balance/balance_seeds3.c similarity index 100% rename from example/balance_seeds/balance_seeds3.c rename to example/balance/balance_seeds3.c diff --git a/example/balance_seeds/Makefile.am b/example/balance_seeds/Makefile.am deleted file mode 100644 index 62bd03a7f..000000000 --- a/example/balance_seeds/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ - -# This file is part of p4est. -# Makefile.am in example/timings -# included non-recursively from toplevel directory - -if P4EST_ENABLE_BUILD_2D -bin_PROGRAMS += example/balance_seeds/p4est_balance_seeds - -example_balance_seeds_p4est_balance_seeds_SOURCES = \ - example/balance_seeds/balance_seeds2.c - -LINT_CSOURCES += \ - $(example_balance_seeds_p4est_balance_seeds_SOURCES) -endif - -if P4EST_ENABLE_BUILD_3D -bin_PROGRAMS += example/balance_seeds/p8est_balance_seeds - -example_balance_seeds_p8est_balance_seeds_SOURCES = \ - example/balance_seeds/balance_seeds3.c - -LINT_CSOURCES += \ - $(example_balance_seeds_p8est_balance_seeds_SOURCES) -endif diff --git a/example/mesh/Makefile.am b/example/mesh/Makefile.am index 949e2431b..9a237a55b 100644 --- a/example/mesh/Makefile.am +++ b/example/mesh/Makefile.am @@ -6,8 +6,6 @@ if P4EST_ENABLE_BUILD_2D bin_PROGRAMS += example/mesh/p4est_mesh example_mesh_p4est_mesh_SOURCES = example/mesh/mesh2.c - -LINT_CSOURCES += $(example_mesh_p4est_mesh_SOURCES) endif if P4EST_ENABLE_BUILD_3D @@ -16,6 +14,4 @@ bin_PROGRAMS += example/mesh/p8est_mesh \ example_mesh_p8est_mesh_SOURCES = example/mesh/mesh3.c example_mesh_p8est_periodicity_SOURCES = example/mesh/periodicity3.c - -LINT_CSOURCES += $(example_mesh_p8est_mesh_SOURCES) endif diff --git a/example/mesh/mesh2.c b/example/mesh/mesh2.c index 9f1988308..9b5ca243a 100644 --- a/example/mesh/mesh2.c +++ b/example/mesh/mesh2.c @@ -241,7 +241,7 @@ test_mesh (p4est_t * p4est, p4est_ghost_t * ghost, p4est_mesh_t * mesh, if (mesh_btype == P4EST_CONNECT_CORNER) { for (c = 0; c < P4EST_CHILDREN; ++c) { qlid = mesh->quad_to_corner[P4EST_CHILDREN * kl + c]; - SC_CHECK_ABORTF (qlid >= -2 + SC_CHECK_ABORTF (qlid >= -3 && qlid < QpG + lnC, "quad %lld corner %d mismatch", (long long) kl, c); } diff --git a/example/particles/Makefile.am b/example/particles/Makefile.am index 96c62218a..bdbc0d9f2 100644 --- a/example/particles/Makefile.am +++ b/example/particles/Makefile.am @@ -9,8 +9,6 @@ bin_PROGRAMS += \ example_particles_p4est_particles_SOURCES = \ example/particles/particles_global.h \ example/particles/particles2.c - -LINT_CSOURCES += $(example_particles_p4est_particles_SOURCES) endif if P4EST_ENABLE_BUILD_3D @@ -19,8 +17,6 @@ bin_PROGRAMS += \ example_particles_p8est_particles_SOURCES = \ example/particles/particles_global.h \ example/particles/particles3.c - -LINT_CSOURCES += $(example_particles_p8est_particles_SOURCES) endif ## from example/steps for future reference diff --git a/example/particles/particles2.c b/example/particles/particles2.c index ec9098988..c20900306 100644 --- a/example/particles/particles2.c +++ b/example/particles/particles2.c @@ -68,7 +68,7 @@ typedef struct qu_data double d; } u; - /** counts of local particles remaining on this quadrant and recieved ones */ + /** counts of local particles remaining on this quadrant and received ones */ p4est_locidx_t premain, preceive; } qu_data_t; diff --git a/example/points/Makefile.am b/example/points/Makefile.am index 06a1dccbb..12f186325 100644 --- a/example/points/Makefile.am +++ b/example/points/Makefile.am @@ -7,12 +7,14 @@ if P4EST_ENABLE_BUILD_2D bin_PROGRAMS += example/points/p4est_points example_points_p4est_points_SOURCES = example/points/points2.c -LINT_CSOURCES += $(example_points_p4est_points_SOURCES) +bin_PROGRAMS += example/points/p4est_points_generate +example_points_p4est_points_generate_SOURCES = example/points/generate_points2.c endif if P4EST_ENABLE_BUILD_3D bin_PROGRAMS += example/points/p8est_points example_points_p8est_points_SOURCES = example/points/points3.c -LINT_CSOURCES += $(example_points_p8est_points_SOURCES) +bin_PROGRAMS += example/points/p8est_points_generate +example_points_p8est_points_generate_SOURCES = example/points/generate_points3.c endif diff --git a/example/points/generate_points2.c b/example/points/generate_points2.c new file mode 100644 index 000000000..96d7fca6e --- /dev/null +++ b/example/points/generate_points2.c @@ -0,0 +1,254 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifdef P4_TO_P8 +#include +#include +#include +#else +#include +#include +#include +#endif /* !P4_TO_P8 */ +#include + +/* Please see doc/example_points.dox for a documentation of this program. */ + +static void +generate_points (const char *filename, + p4est_connectivity_t * conn, + p4est_gloidx_t global_num_points, sc_MPI_Comm mpicomm) +{ + int mpiret; + int num_procs, rank; + int count; + p4est_gloidx_t offset_mine, offset_next; + p4est_locidx_t local_num_points; + p4est_locidx_t u; + double *point_buffer; + double theta; +#ifdef P4_TO_P8 + double phi; +#else + double dtheta; +#endif + sc_MPI_File file_handle; + sc_MPI_Offset mpi_offset; + + mpiret = sc_MPI_Comm_size (mpicomm, &num_procs); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Comm_rank (mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + /* open a file (create if the file does not exist) */ + mpiret = sc_io_open (mpicomm, filename, SC_IO_WRITE_CREATE, + sc_MPI_INFO_NULL, &file_handle); + SC_CHECK_MPI (mpiret); + + /* local (MPI) number of points */ + local_num_points = global_num_points / num_procs; + + /* offset to first point of current MPI process */ + offset_mine = p4est_partition_cut_gloidx (global_num_points, + rank, num_procs); + + /* offset to first point of successor MPI process */ + offset_next = p4est_partition_cut_gloidx (global_num_points, + rank + 1, num_procs); + local_num_points = (p4est_locidx_t) (offset_next - offset_mine); + + /* allocate buffer for point's coordinates */ + point_buffer = P4EST_ALLOC (double, 3 * local_num_points); + + /* set file offset (in bytes) for this calling process */ + /* *INDENT-OFF* HORRIBLE indent bug */ + mpi_offset = (sc_MPI_Offset) offset_mine * 3 * sizeof (double); + /* *INDENT-ON* */ + +#ifndef P4_TO_P8 + /* 2D */ + dtheta = (2. * M_PI) / global_num_points; + + for (u = 0; u < local_num_points; ++u) { + theta = (offset_mine + u) * dtheta; + + point_buffer[3 * u + 0] = 0.5 + 0.25 * cos (theta); + point_buffer[3 * u + 1] = 0.5 + 0.25 * sin (2 * theta); + point_buffer[3 * u + 2] = 0.0; + } + +#else + + /* 3D */ + for (u = 0; u < local_num_points; ++u) { + theta = 2. * M_PI * rand () / (RAND_MAX + 1.0); + phi = M_PI * rand () / (RAND_MAX + 1.0); + + point_buffer[3 * u + 0] = 0.5 + 0.25 * cos (theta) * sin (phi); + point_buffer[3 * u + 1] = 0.5 + 0.25 * sin (theta) * sin (phi); + point_buffer[3 * u + 2] = 0.5 + 0.25 * cos (phi); + } +#endif + + if (rank == 0) { + /* write the global number number of points */ + mpiret = sc_io_write_at (file_handle, 0, &global_num_points, + sizeof (p4est_gloidx_t), sc_MPI_BYTE, &count); + SC_CHECK_MPI (mpiret); + SC_CHECK_ABORT (count == (int) sizeof (p4est_gloidx_t), + "Write number of global points: count mismatch"); + } + + /* each MPI process writes its data for its own offset */ + mpiret = + sc_io_write_at_all (file_handle, mpi_offset + sizeof (p4est_gloidx_t), + &point_buffer[0], + 3 * local_num_points * sizeof (double), sc_MPI_BYTE, + &count); + SC_CHECK_MPI (mpiret); + SC_CHECK_ABORT (count == (int) (3 * local_num_points * sizeof (double)), + "Write points: count mismatch"); + + P4EST_FREE (point_buffer); + + /* close the file collectively */ + mpiret = sc_io_close (&file_handle); + SC_CHECK_MPI (mpiret); +} + +int +main (int argc, char **argv) +{ + int mpiret; + int num_procs, rank; + int wrongusage; + char buffer[BUFSIZ]; + p4est_gloidx_t global_num_points; + p4est_connectivity_t *conn; + sc_MPI_Comm mpicomm; + const char *usage; + + /* initialize MPI and p4est internals */ + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + mpicomm = sc_MPI_COMM_WORLD; + mpiret = sc_MPI_Comm_size (mpicomm, &num_procs); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Comm_rank (mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT); + p4est_init (NULL, SC_LP_DEFAULT); + + /* process command line arguments */ + usage = + "Arguments: \n" + " Configuration can be any of\n" +#ifndef P4_TO_P8 + " unit|brick|three|moebius|star|periodic\n" +#else + " unit|brick|periodic|rotwrap|twocubes|rotcubes\n" +#endif + " Globalnumpoints is the total number of points generated\n" + " over all MPI process, >= 0\n" + " Prefix is for writing a point data file (using MPI-IO)\n"; + wrongusage = 0; + if (!wrongusage && argc != 4) { + P4EST_GLOBAL_LERROR ("Invalid command line argument count\n"); + wrongusage = 1; + } + conn = NULL; + if (!wrongusage) { +#ifndef P4_TO_P8 + if (!strcmp (argv[1], "unit")) { + conn = p4est_connectivity_new_unitsquare (); + } + else if (!strcmp (argv[1], "brick")) { + conn = p4est_connectivity_new_brick (2, 3, 0, 0); + } + else if (!strcmp (argv[1], "three")) { + conn = p4est_connectivity_new_corner (); + } + else if (!strcmp (argv[1], "moebius")) { + conn = p4est_connectivity_new_moebius (); + } + else if (!strcmp (argv[1], "star")) { + conn = p4est_connectivity_new_star (); + } + else if (!strcmp (argv[1], "periodic")) { + conn = p4est_connectivity_new_periodic (); + } +#else + if (!strcmp (argv[1], "unit")) { + conn = p8est_connectivity_new_unitcube (); + } + else if (!strcmp (argv[1], "brick")) { + conn = p8est_connectivity_new_brick (2, 3, 4, 0, 0, 0); + } + else if (!strcmp (argv[1], "periodic")) { + conn = p8est_connectivity_new_periodic (); + } + else if (!strcmp (argv[1], "rotwrap")) { + conn = p8est_connectivity_new_rotwrap (); + } + else if (!strcmp (argv[1], "twocubes")) { + conn = p8est_connectivity_new_twocubes (); + } + else if (!strcmp (argv[1], "rotcubes")) { + conn = p8est_connectivity_new_rotcubes (); + } +#endif + else { + P4EST_GLOBAL_LERROR ("Invalid connectivity configuration\n"); + wrongusage = 1; + } + } + if (!wrongusage) { + global_num_points = (p4est_gloidx_t) atol (argv[2]); + if (global_num_points <= -1) { + P4EST_GLOBAL_LERROR ("Invalid global number of points\n"); + wrongusage = 1; + } + } + if (wrongusage) { + P4EST_GLOBAL_LERROR (usage); + sc_abort_collective ("Usage error"); + } + + SC_GLOBAL_PRODUCTIONF ("Write %lld total points\n", + (long long) global_num_points); + snprintf (buffer, BUFSIZ, "%s.pts", argv[3]); + generate_points (buffer, conn, global_num_points, mpicomm); + + /* in the present version of this program the connectivity is not used */ + p4est_connectivity_destroy (conn); + + /* clean up and exit */ + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + + return 0; +} diff --git a/example/points/generate_points3.c b/example/points/generate_points3.c new file mode 100644 index 000000000..42e76738a --- /dev/null +++ b/example/points/generate_points3.c @@ -0,0 +1,28 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* Please see doc/example_points.dox for a documentation of this program. */ + +#include +#include "generate_points2.c" diff --git a/example/points/points2.c b/example/points/points2.c index d836a79e1..a2fb23f5d 100644 --- a/example/points/points2.c +++ b/example/points/points2.c @@ -22,14 +22,6 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/******************************************************************** - * IMPORTANT NOTE * - * * - * The p4est_points functionality depends on sc/src/sc_sort. * - * That parallel bitonic sort is still buggy (see sc/bugs). * - * If you want to use this code you have to fix the sort first. * - ********************************************************************/ - #ifdef P4_TO_P8 #include #include @@ -45,6 +37,7 @@ * Usage: p4est_points * possible configurations: * o unit Refinement on the unit square. + * o brick Refinement on a 2 by 3 (by 4) brick. * o three Refinement on a forest with three trees. * o moebius Refinement on a 5-tree Moebius band. * o star Refinement on a 6-tree star shaped domain. @@ -52,33 +45,88 @@ */ static p4est_quadrant_t * -read_points (const char *filename, p4est_topidx_t num_trees, - p4est_locidx_t * num_points) +read_points (const char *filename, + p4est_connectivity_t *conn, + const char *conn_name, + p4est_locidx_t * local_num_points, + sc_MPI_Comm mpicomm) { - int retval; + int mpiret; int qshift; + int num_procs, rank; + int is_brick_connectivity; + int count; unsigned ucount, u, ignored; + p4est_gloidx_t global_num_points; + p4est_gloidx_t offset_mine, offset_next; double x, y, z; double qlen; double *point_buffer; p4est_quadrant_t *points, *q; - FILE *file; + sc_MPI_File file_handle; + sc_MPI_Offset mpi_offset; + + /* special treament for brick connectivity */ + if(!strcmp (conn_name, "brick")) + is_brick_connectivity = 1; + else + is_brick_connectivity = 0; + + mpiret = sc_MPI_Comm_size (mpicomm, &num_procs); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Comm_rank (mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + /* open a file */ + mpiret = sc_io_open (mpicomm, filename, SC_IO_READ, sc_MPI_INFO_NULL, + &file_handle); + SC_CHECK_MPI (mpiret); + + if (rank == 0) { + /* read the global number of points from file */ + mpiret = sc_io_read_at (file_handle, 0, &global_num_points, + sizeof (p4est_gloidx_t), sc_MPI_BYTE, &count); + SC_CHECK_MPI (mpiret); + SC_CHECK_ABORT (count == (int) sizeof (p4est_gloidx_t), + "Read number of global points: count mismatch"); + } + /* broadcast the global number of points */ + mpiret = sc_MPI_Bcast (&global_num_points, sizeof (p4est_gloidx_t), + sc_MPI_BYTE, 0, mpicomm); + SC_CHECK_MPI (mpiret); + + /* offset to first point of current MPI process */ + offset_mine = p4est_partition_cut_gloidx (global_num_points, + rank, num_procs); - file = fopen (filename, "rb"); - SC_CHECK_ABORTF (file != NULL, "Open file %s", filename); + /* offset to first point of successor MPI process */ + offset_next = p4est_partition_cut_gloidx (global_num_points, + rank + 1, num_procs); + *local_num_points = (p4est_locidx_t) (offset_next - offset_mine); - sc_fread (&ucount, sizeof (unsigned int), 1, file, "Read point count"); + /* allocate buffer for point's coordinates */ + point_buffer = P4EST_ALLOC (double, 3 * (*local_num_points)); - point_buffer = P4EST_ALLOC (double, 3 * ucount); - sc_fread (point_buffer, sizeof (double), (size_t) (3 * ucount), file, - "Read points"); + /* set file offset (in bytes) for this calling process */ + mpi_offset = (sc_MPI_Offset) offset_mine * 3 * sizeof(double); - retval = fclose (file); - SC_CHECK_ABORTF (retval == 0, "Close file %s", filename); + /* each mpi process reads its data for its own offset */ + mpiret = sc_io_read_at_all (file_handle, mpi_offset + sizeof (p4est_gloidx_t), + &point_buffer[0], 3 * *local_num_points * sizeof (double), + sc_MPI_BYTE, &count); + SC_CHECK_MPI (mpiret); + SC_CHECK_ABORT (count == (int) (3 * *local_num_points * sizeof (double)), + "Read points: count mismatch"); + + /* close the file collectively */ + mpiret = sc_io_close (&file_handle); + SC_CHECK_MPI (mpiret); + ucount = *local_num_points; q = points = P4EST_ALLOC_ZERO (p4est_quadrant_t, ucount); qlen = (double) (1 << P4EST_QMAXLEVEL); qshift = P4EST_MAXLEVEL - P4EST_QMAXLEVEL; + for (ignored = u = 0; u < ucount; ++u) { x = point_buffer[3 * u + 0]; y = point_buffer[3 * u + 1]; @@ -92,26 +140,28 @@ read_points (const char *filename, p4est_topidx_t num_trees, q->x = SC_MIN (q->x, P4EST_ROOT_LEN - 1); q->y = (p4est_qcoord_t) (y * qlen) << qshift; q->y = SC_MIN (q->y, P4EST_ROOT_LEN - 1); + #ifdef P4_TO_P8 q->z = (p4est_qcoord_t) (z * qlen) << qshift; q->z = SC_MIN (q->z, P4EST_ROOT_LEN - 1); #endif q->level = P4EST_MAXLEVEL; - q->p.which_tree = - (p4est_topidx_t) ((double) num_trees * rand () / (RAND_MAX + 1.0)); + q->p.which_tree = is_brick_connectivity == 1 ? 0 : + (p4est_topidx_t) ((double) conn->num_trees * rand () / (RAND_MAX + 1.0)); P4EST_ASSERT (p4est_quadrant_is_node (q, 1)); ++q; } P4EST_FREE (point_buffer); - if (num_points != NULL) { - *num_points = (p4est_locidx_t) (ucount - ignored); + if (local_num_points != NULL) { + *local_num_points = (p4est_locidx_t) (ucount - ignored); } return points; } + int main (int argc, char **argv) { @@ -120,7 +170,7 @@ main (int argc, char **argv) int maxlevel; int wrongusage; char buffer[BUFSIZ]; - p4est_locidx_t num_points, max_points; + p4est_locidx_t local_num_points, max_points; p4est_connectivity_t *conn; p4est_quadrant_t *points; p4est_t *p4est; @@ -144,16 +194,24 @@ main (int argc, char **argv) "Arguments: \n" " Configuration can be any of\n" #ifndef P4_TO_P8 - " unit|three|moebius|star|periodic\n" + " unit|brick|three|moebius|star|periodic\n" #else - " unit|periodic|rotwrap|twocubes|rotcubes\n" + " unit|brick|periodic|rotwrap|twocubes|rotcubes\n" #endif " Level controls the maximum depth of refinement\n" " Maxpoints is the maximum number of points per quadrant\n" " which applies to all quadrants above maxlevel\n" " A value of 0 refines recursively to maxlevel\n" " A value of -1 does no refinement at all\n" - " Prefix is for loading a point data file\n"; + " Prefix is for loading a point data file (prefix.pts)\n" + " generate_points2 or generate_points3 are helper to create points files.\n" + "\n" + "Example:\n" + " - generate 10000 test points:\n" + " ./generate_points2 unit 10000 test\n" + " - run example up to level=10 :\n" + " mpirun -np 4 ./points2 unit 10 100 ./test\n"; + wrongusage = 0; if (!wrongusage && argc != 5) { wrongusage = 1; @@ -164,6 +222,9 @@ main (int argc, char **argv) if (!strcmp (argv[1], "unit")) { conn = p4est_connectivity_new_unitsquare (); } + else if (!strcmp (argv[1], "brick")) { + conn = p4est_connectivity_new_brick (2,3,0,0); + } else if (!strcmp (argv[1], "three")) { conn = p4est_connectivity_new_corner (); } @@ -180,6 +241,9 @@ main (int argc, char **argv) if (!strcmp (argv[1], "unit")) { conn = p8est_connectivity_new_unitcube (); } + else if (!strcmp (argv[1], "brick")) { + conn = p8est_connectivity_new_brick (2,3,4,0,0,0); + } else if (!strcmp (argv[1], "periodic")) { conn = p8est_connectivity_new_periodic (); } @@ -214,21 +278,21 @@ main (int argc, char **argv) sc_abort_collective ("Usage error"); } - snprintf (buffer, BUFSIZ, "%s%d_%d.pts", argv[4], rank, num_procs); - points = read_points (buffer, conn->num_trees, &num_points); - SC_LDEBUGF ("Read %lld points\n", (long long) num_points); + snprintf (buffer, BUFSIZ, "%s.pts", argv[4]); + points = read_points (buffer, conn, argv[1], &local_num_points, mpicomm); + SC_LDEBUGF ("Read %lld points\n", (long long) local_num_points); p4est = p4est_new_points (mpicomm, conn, maxlevel, points, - num_points, max_points, 5, NULL, NULL); + local_num_points, max_points, 5, NULL, NULL); P4EST_FREE (points); p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_points_created"); - p4est_partition (p4est, 0, NULL); - p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_points_partition"); - p4est_balance (p4est, P4EST_CONNECT_FULL, NULL); p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_points_balance"); + p4est_partition (p4est, 0, NULL); + p4est_vtk_write_file (p4est, NULL, P4EST_STRING "_points_partition"); + p4est_destroy (p4est); p4est_connectivity_destroy (conn); diff --git a/example/search/Makefile.am b/example/search/Makefile.am new file mode 100644 index 000000000..0ca6bf6fd --- /dev/null +++ b/example/search/Makefile.am @@ -0,0 +1,18 @@ + +# This file is part of p4est. +# Makefile.am in example/search +# included non-recursively from toplevel directory + +if P4EST_ENABLE_BUILD_2D +bin_PROGRAMS += example/search/p4est_count_quadrants + +example_search_p4est_count_quadrants_SOURCES = \ + example/search/count_quadrants2.c +endif + +if P4EST_ENABLE_BUILD_3D +bin_PROGRAMS += example/search/p8est_count_quadrants + +example_search_p8est_count_quadrants_SOURCES = \ + example/search/count_quadrants3.c +endif diff --git a/example/search/count_quadrants2.c b/example/search/count_quadrants2.c new file mode 100644 index 000000000..c0272e65f --- /dev/null +++ b/example/search/count_quadrants2.c @@ -0,0 +1,259 @@ + +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file count_quadrants2.c + * + * This 2D example program counts all quadrants in a given tree structure + * using p4est search routine(s). + * Both branch quadrants and leaf quadrants are added separetely. + * Leaf quadrant are always local to a given process, so their global number + * is independent of the partition of any given mesh refinement structure. + * We count branch quadrants only for the process that owns a first descendant, + * which means that the number of branch quadrants is also partition independent. + * This can be verified manually by comparing the output for multiple MPI sizes. + */ + +#ifndef P4_TO_P8 +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +/** + * Data structure to store quadrant statistics. + */ +typedef struct quadrant_stats +{ + p4est_t *p4est; + p4est_locidx_t local_branch_count; + p4est_locidx_t local_leaf_count; + p4est_locidx_t local_aggregate_count; + p4est_gloidx_t global_aggregate_count; +} +quadrant_stats_t; + +/** + * Call-back routine to record quadrant count during the search routine + * execution. + * Recursion is allowed to continue in case of a branch quadrant. + */ +static int +count_callback (p4est_t * p4est, p4est_topidx_t which_tree, + p4est_quadrant_t * quadrant, p4est_locidx_t local_num, + void *point) +{ + quadrant_stats_t *qs; + qs = (quadrant_stats_t *) p4est->user_pointer; + P4EST_ASSERT (qs->p4est == p4est); + + if (local_num == -1) { + /** + * In the event a branch quadrant is owned by multiple ranks, + * p4est_comm_is_owner always returns true for the lowest rank. + * This property is useful to determine the unique ownership of + * branch quadrants. + * A branch quadrant is counted only by its unique owner rank. + */ + if (p4est_comm_is_owner (p4est, which_tree, quadrant, p4est->mpirank)) { + ++qs->local_branch_count; + } + + return 1; + } + + /* Leaf quadrants always have a unique owner. */ + ++qs->local_leaf_count; + + return 0; +} + +/** + * Routine to count all(branch/leaf) quadrants in a forest structure. + */ +static void +count_quadrants (p4est_t * p4est) +{ + quadrant_stats_t *qs; + p4est_gloidx_t gac; +#ifdef P4EST_ENABLE_DEBUG + p4est_gloidx_t glc, global_leaf_count; +#endif + + qs = (quadrant_stats_t *) p4est->user_pointer; + P4EST_ASSERT (qs->p4est == p4est); + + /** + * Traverse the tree structure of the forest in a way we visit + * every quadrant only once. + */ + p4est_search_reorder (p4est, 0, NULL, count_callback, NULL, NULL, NULL); + qs->local_aggregate_count = qs->local_leaf_count + qs->local_branch_count; + + /** + * Gather counts from other processors and do summation. + * The result is broadcasted to every processor. + */ + gac = qs->local_aggregate_count; + sc_MPI_Allreduce (&gac, &qs->global_aggregate_count, 1, P4EST_MPI_GLOIDX, + sc_MPI_SUM, sc_MPI_COMM_WORLD); + + /** + * Sanity check to ensure leaf count is consistent with the + * forest structure. + */ +#ifdef P4EST_ENABLE_DEBUG + glc = qs->local_leaf_count; + sc_MPI_Allreduce (&glc, &global_leaf_count, 1, P4EST_MPI_GLOIDX, + sc_MPI_SUM, sc_MPI_COMM_WORLD); + P4EST_ASSERT (global_leaf_count == p4est->global_num_quadrants); +#endif +} + +int +main (int argc, char **argv) +{ + int mpiret; + int level, wrong_usage; + const char *usage; + p4est_t *p4est; + p4est_connectivity_t *conn; + p4est_geometry_t *geom; + quadrant_stats_t qs; + + /* MPI initialization. */ + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + + /* Package init. */ + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEFAULT); + p4est_init (NULL, SC_LP_DEFAULT); + + /* Process command line arguments. */ + /* *INDENT-OFF* */ + usage = + "Arguments: \n" + " Connectivity can be any of\n" +#ifndef P4_TO_P8 + " unit|three|moebius|\n" +#else + " unit|twocubes|rotcubes|sphere\n" +#endif + " Level controls the maximum depth of refinement\n"; + /* *INDENT-ON* */ + wrong_usage = 0; + + /* Query basic parameters. */ + if (!wrong_usage && argc != 3) { + P4EST_GLOBAL_LERROR ("Invalid argument list\n"); + wrong_usage = 1; + } + if (!wrong_usage && + ((level = atoi (argv[2])) < 0 || level > P4EST_QMAXLEVEL)) { + P4EST_GLOBAL_LERRORF ("Level out of bounds 0..%d\n", P4EST_QMAXLEVEL); + wrong_usage = 1; + } + + /* Create connectivity. */ + geom = NULL; + if (!wrong_usage) { + if (!strcmp (argv[1], "unit")) { +#ifndef P4_TO_P8 + conn = p4est_connectivity_new_unitsquare (); +#else + conn = p8est_connectivity_new_unitcube (); +#endif + } +#ifndef P4_TO_P8 + else if (!strcmp (argv[1], "three")) { + conn = p4est_connectivity_new_corner (); + } + else if (!strcmp (argv[1], "moebius")) { + conn = p4est_connectivity_new_moebius (); + } +#else + else if (!strcmp (argv[1], "twocubes")) { + conn = p8est_connectivity_new_twocubes (); + } + else if (!strcmp (argv[1], "rotcubes")) { + conn = p8est_connectivity_new_rotcubes (); + } + else if (!strcmp (argv[1], "sphere")) { + conn = p8est_connectivity_new_sphere (); + geom = p8est_geometry_new_sphere (conn, 1., 0.191728, 0.039856); + } +#endif + else { + P4EST_GLOBAL_LERROR ("Invalid connectivity\n"); + wrong_usage = 1; + } + } + if (wrong_usage) { + P4EST_GLOBAL_LERROR (usage); + sc_abort_collective ("Usage error"); + } + + /* Create p4est. */ + p4est = p4est_new_ext (sc_MPI_COMM_WORLD, conn, 0, level, 1, 0, NULL, NULL); + p4est_partition (p4est, 1, NULL); + p4est_vtk_write_file (p4est, geom, P4EST_STRING "_quadrant_count"); + + /* Count quadrants. */ + memset (&qs, 0, sizeof (qs)); + qs.p4est = p4est; + p4est->user_pointer = &qs; + count_quadrants (p4est); + + /* Print quadrant count statistics for current rank. */ + P4EST_INFOF ("Local leaf quadrant count = %ld\n", + (long) qs.local_leaf_count); + P4EST_INFOF ("Local branch quadrant count = %ld\n", + (long) qs.local_branch_count); + + /* Print total quadrants (leaf + branch) in the tree structure. */ + mpiret = sc_MPI_Barrier (p4est->mpicomm); + SC_CHECK_MPI (mpiret); + P4EST_GLOBAL_PRODUCTIONF ("Global aggregrate quadrant count = %lld\n", + (long long) qs.global_aggregate_count); + + /* Free memory. */ + if (geom != NULL) { + p4est_geometry_destroy (geom); + } + p4est_destroy (p4est); + p4est_connectivity_destroy (conn); + + /* Close MPI environment. */ + sc_finalize (); + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + + return EXIT_SUCCESS; +} diff --git a/example/search/count_quadrants3.c b/example/search/count_quadrants3.c new file mode 100644 index 000000000..6a04fcd08 --- /dev/null +++ b/example/search/count_quadrants3.c @@ -0,0 +1,34 @@ + +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file count_quadrants3.c + * + * This 3D example program counts all quadrants in a given tree structure + * using p4est search routine(s). + * Both branch quadrants and leaf quadrants are considered. + */ + +#include +#include "count_quadrants2.c" diff --git a/example/simple/Makefile.am b/example/simple/Makefile.am index 9c78f66b7..d283b8c7d 100644 --- a/example/simple/Makefile.am +++ b/example/simple/Makefile.am @@ -6,13 +6,9 @@ if P4EST_ENABLE_BUILD_2D bin_PROGRAMS += example/simple/p4est_simple example_simple_p4est_simple_SOURCES = example/simple/simple2.c - -LINT_CSOURCES += $(example_simple_p4est_simple_SOURCES) endif if P4EST_ENABLE_BUILD_3D bin_PROGRAMS += example/simple/p8est_simple example_simple_p8est_simple_SOURCES = example/simple/simple3.c - -LINT_CSOURCES += $(example_simple_p8est_simple_SOURCES) endif diff --git a/example/simple/simple2.c b/example/simple/simple2.c index fac364c6a..16e518fcc 100644 --- a/example/simple/simple2.c +++ b/example/simple/simple2.c @@ -26,6 +26,7 @@ * Usage: p4est_simple * possible configurations: * o unit Refinement on the unit square. + * o brick Refinement on a regular forest of octrees. * o three Refinement on a forest with three trees. * o evil Check second round of refinement with np=5 level=7 * o evil3 Check second round of refinement on three trees @@ -39,9 +40,13 @@ * o pdisk Refinement on a 5-tree spherical disk, periodic b.c. * o periodic Refinement on the unit square with all-periodic b.c. * o rotwrap Refinement on the unit square with weird periodic b.c. - * o icosahedron Refinement on the sphere + * o circle Refinement on a 6-tree donut-like circle. + * o drop Refinement on a 5-trees geometry with an inner hole. + * o icosahedron Refinement on the icosahedron sphere with geometry. * o shell2d Refinement on a 2d shell with geometry. * o disk2d Refinement on a 2d disk with geometry. + * o bowtie Refinement on a 2-tree bowtie domain. + * o sphere2d Refinement on a 6-tree sphere surface with geometry. */ #include @@ -52,6 +57,7 @@ typedef enum { P4EST_CONFIG_NULL, P4EST_CONFIG_UNIT, + P4EST_CONFIG_BRICK, P4EST_CONFIG_THREE, P4EST_CONFIG_EVIL, P4EST_CONFIG_EVIL3, @@ -65,9 +71,13 @@ typedef enum P4EST_CONFIG_PDISK, P4EST_CONFIG_PERIODIC, P4EST_CONFIG_ROTWRAP, + P4EST_CONFIG_CIRCLE, + P4EST_CONFIG_DROP, P4EST_CONFIG_ICOSAHEDRON, P4EST_CONFIG_SHELL2D, P4EST_CONFIG_DISK2D, + P4EST_CONFIG_BOWTIE, + P4EST_CONFIG_SPHERE2D, P4EST_CONFIG_LAST } simple_config_t; @@ -119,6 +129,9 @@ static const simple_regression_t regression[] = { P4EST_CONFIG_PDISK, 5, 5, 0x507fd0c9 }, { P4EST_CONFIG_ROTWRAP, 1, 6, 0x9dd600c5U }, { P4EST_CONFIG_ROTWRAP, 3, 6, 0x9dd600c5U }, + { P4EST_CONFIG_CIRCLE, 3, 6, 0xd6e4931b }, + { P4EST_CONFIG_DROP, 3, 6, 0xea6a6726 }, + { P4EST_CONFIG_BOWTIE, 1, 3, 0x63ba0805 }, { P4EST_CONFIG_NULL, 0, 0, 0 }}; /* *INDENT-ON* */ @@ -262,6 +275,7 @@ main (int argc, char **argv) p4est_coarsen_t coarsen_fn; simple_config_t config; const simple_regression_t *r; + int nbrick_x = 1, nbrick_y = 1; /* initialize MPI and p4est internals */ mpiret = sc_MPI_Init (&argc, &argv); @@ -279,19 +293,22 @@ main (int argc, char **argv) usage = "Arguments: \n" " Configuration can be any of\n" - " unit|three|evil|evil3|pillow|moebius|\n" + " unit|brick|three|evil|evil3|pillow|moebius|\n" " star|cubed|disk|xdisk|ydisk|pdisk|periodic|\n" - " rotwrap|icosahedron|shell2d|disk2d\n" + " rotwrap|circle|drop|icosahedron|shell2d|disk2d|bowtie|sphere2d\n" " Level controls the maximum depth of refinement\n"; wrongusage = 0; config = P4EST_CONFIG_NULL; - if (!wrongusage && argc != 3) { + if (!wrongusage && argc < 3) { wrongusage = 1; } if (!wrongusage) { if (!strcmp (argv[1], "unit")) { config = P4EST_CONFIG_UNIT; } + else if (!strcmp (argv[1], "brick")) { + config = P4EST_CONFIG_BRICK; + } else if (!strcmp (argv[1], "three")) { config = P4EST_CONFIG_THREE; } @@ -331,6 +348,12 @@ main (int argc, char **argv) else if (!strcmp (argv[1], "rotwrap")) { config = P4EST_CONFIG_ROTWRAP; } + else if (!strcmp (argv[1], "circle")) { + config = P4EST_CONFIG_CIRCLE; + } + else if (!strcmp (argv[1], "drop")) { + config = P4EST_CONFIG_DROP; + } else if (!strcmp (argv[1], "icosahedron")) { config = P4EST_CONFIG_ICOSAHEDRON; } @@ -340,6 +363,12 @@ main (int argc, char **argv) else if (!strcmp (argv[1], "disk2d")) { config = P4EST_CONFIG_DISK2D; } + else if (!strcmp (argv[1], "bowtie")) { + config = P4EST_CONFIG_BOWTIE; + } + else if (!strcmp (argv[1], "sphere2d")) { + config = P4EST_CONFIG_SPHERE2D; + } else { wrongusage = 1; } @@ -370,7 +399,12 @@ main (int argc, char **argv) /* create connectivity and forest structures */ geom = NULL; - if (config == P4EST_CONFIG_THREE || config == P4EST_CONFIG_EVIL3) { + if (config == P4EST_CONFIG_BRICK) { + nbrick_x = argc > 3 ? atoi (argv[3]) : 3; + nbrick_y = argc > 4 ? atoi (argv[4]) : 2; + connectivity = p4est_connectivity_new_brick (nbrick_x, nbrick_y, 0, 0); + } + else if (config == P4EST_CONFIG_THREE || config == P4EST_CONFIG_EVIL3) { connectivity = p4est_connectivity_new_corner (); } else if (config == P4EST_CONFIG_PILLOW) { @@ -403,6 +437,12 @@ main (int argc, char **argv) else if (config == P4EST_CONFIG_ROTWRAP) { connectivity = p4est_connectivity_new_rotwrap (); } + else if (config == P4EST_CONFIG_CIRCLE) { + connectivity = p4est_connectivity_new_circle (); + } + else if (config == P4EST_CONFIG_DROP) { + connectivity = p4est_connectivity_new_drop (); + } else if (config == P4EST_CONFIG_ICOSAHEDRON) { double R = 1.0; /* sphere radius default value */ @@ -420,9 +460,20 @@ main (int argc, char **argv) connectivity = p4est_connectivity_new_disk2d (); geom = p4est_geometry_new_disk2d (connectivity, 0.44, 1.0); } + else if (config == P4EST_CONFIG_BOWTIE) { + connectivity = p4est_connectivity_new_bowtie (); + } + else if (config == P4EST_CONFIG_SPHERE2D) { + connectivity = p4est_connectivity_new_cubed (); + geom = p4est_geometry_new_sphere2d (connectivity, 1.0); + } else { connectivity = p4est_connectivity_new_unitsquare (); } + + /* create forest data structure */ + P4EST_GLOBAL_PRODUCTIONF ("Size of one quadrant: %d bytes\n", + (int) sizeof (p4est_quadrant_t)); p4est = p4est_new_ext (mpi->mpicomm, connectivity, 15, 0, 0, sizeof (user_data_t), init_fn, geom); p4est_vtk_write_file (p4est, geom, "simple2_new"); diff --git a/example/simple/simple3.c b/example/simple/simple3.c index 7328d605e..f832ee918 100644 --- a/example/simple/simple3.c +++ b/example/simple/simple3.c @@ -26,6 +26,7 @@ * Usage: p8est_simple * possible configurations: * o unit The unit cube. + * o brick The brick connectivity. * o periodic The unit cube with all-periodic boundary conditions. * o rotwrap The unit cube with various self-periodic b.c. * o twocubes Two connected cubes. @@ -48,8 +49,10 @@ typedef enum { P8EST_CONFIG_NULL, P8EST_CONFIG_UNIT, + P8EST_CONFIG_BRICK, P8EST_CONFIG_PERIODIC, P8EST_CONFIG_ROTWRAP, + P8EST_CONFIG_DROP, P8EST_CONFIG_TWOCUBES, P8EST_CONFIG_TWOWRAP, P8EST_CONFIG_ROTCUBES, @@ -100,6 +103,7 @@ static const simple_regression_t regression[] = { P8EST_CONFIG_ROTWRAP, 1, 5, 0xe4d123b2U }, { P8EST_CONFIG_ROTWRAP, 3, 5, 0xe4d123b2U }, { P8EST_CONFIG_ROTWRAP, 5, 6, 0x81c22cc6U }, + { P8EST_CONFIG_DROP, 1, 5, 0x81c22cc6U }, { P8EST_CONFIG_ROTCUBES, 1, 5, 0x5c497bdaU }, { P8EST_CONFIG_ROTCUBES, 3, 5, 0x5c497bdaU }, { P8EST_CONFIG_ROTCUBES, 5, 6, 0x00530556U }, @@ -176,6 +180,7 @@ main (int argc, char **argv) p8est_coarsen_t coarsen_fn; simple_config_t config; const simple_regression_t *r; + int nbrick_x = 1, nbrick_y = 1, nbrick_z = 1; /* initialize MPI and p4est internals */ mpiret = sc_MPI_Init (&argc, &argv); @@ -193,23 +198,29 @@ main (int argc, char **argv) usage = "Arguments: \n" " Configuration can be any of\n" - " unit|periodic|rotwrap|twocubes|twowrap|rotcubes|shell|sphere|torus\n" + " unit|brick|periodic|rotwrap|drop|twocubes|twowrap|rotcubes|shell|sphere|torus\n" " Level controls the maximum depth of refinement\n"; wrongusage = 0; config = P8EST_CONFIG_NULL; - if (!wrongusage && argc != 3) { + if (!wrongusage && argc < 3) { wrongusage = 1; } if (!wrongusage) { if (!strcmp (argv[1], "unit")) { config = P8EST_CONFIG_UNIT; } + else if (!strcmp (argv[1], "brick")) { + config = P8EST_CONFIG_BRICK; + } else if (!strcmp (argv[1], "periodic")) { config = P8EST_CONFIG_PERIODIC; } else if (!strcmp (argv[1], "rotwrap")) { config = P8EST_CONFIG_ROTWRAP; } + else if (!strcmp (argv[1], "drop")) { + config = P8EST_CONFIG_DROP; + } else if (!strcmp (argv[1], "twocubes")) { config = P8EST_CONFIG_TWOCUBES; } @@ -244,12 +255,22 @@ main (int argc, char **argv) /* create connectivity and forest structures */ geom = NULL; - if (config == P8EST_CONFIG_PERIODIC) { + if (config == P8EST_CONFIG_BRICK) { + nbrick_x = argc > 3 ? atoi (argv[3]) : 3; + nbrick_y = argc > 4 ? atoi (argv[4]) : 2; + nbrick_z = argc > 5 ? atoi (argv[5]) : 1; + connectivity = + p8est_connectivity_new_brick (nbrick_x, nbrick_y, nbrick_z, 0, 0, 0); + } + else if (config == P8EST_CONFIG_PERIODIC) { connectivity = p8est_connectivity_new_periodic (); } else if (config == P8EST_CONFIG_ROTWRAP) { connectivity = p8est_connectivity_new_rotwrap (); } + else if (config == P8EST_CONFIG_DROP) { + connectivity = p8est_connectivity_new_drop (); + } else if (config == P8EST_CONFIG_TWOCUBES) { connectivity = p8est_connectivity_new_twocubes (); refine_fn = refine_sparse_fn; @@ -276,6 +297,10 @@ main (int argc, char **argv) else { connectivity = p8est_connectivity_new_unitcube (); } + + /* create forest data structure */ + P4EST_GLOBAL_PRODUCTIONF ("Size of one quadrant: %d bytes\n", + (int) sizeof (p8est_quadrant_t)); p8est = p8est_new_ext (mpi->mpicomm, connectivity, 1, 0, 0, sizeof (user_data_t), init_fn, NULL); diff --git a/example/spheres/Makefile.am b/example/spheres/Makefile.am index 3209c71ca..06367cf6b 100644 --- a/example/spheres/Makefile.am +++ b/example/spheres/Makefile.am @@ -17,8 +17,6 @@ bin_PROGRAMS += \ example_spheres_p4est_spheres_SOURCES = \ example/spheres/spheres_global.h \ example/spheres/spheres2.c - -LINT_CSOURCES += $(example_spheres_p4est_spheres_SOURCES) endif if P4EST_ENABLE_BUILD_3D @@ -35,8 +33,6 @@ bin_PROGRAMS += \ example_spheres_p8est_spheres_SOURCES = \ example/spheres/spheres_global.h \ example/spheres/spheres3.c - -LINT_CSOURCES += $(example_spheres_p8est_spheres_SOURCES) endif ## from example/steps for future reference diff --git a/example/steps/Makefile.am b/example/steps/Makefile.am index bc1b8f726..435ac7108 100644 --- a/example/steps/Makefile.am +++ b/example/steps/Makefile.am @@ -8,16 +8,13 @@ bin_PROGRAMS += \ example/steps/p4est_step1 \ example/steps/p4est_step2 \ example/steps/p4est_step3 \ - example/steps/p4est_step4 + example/steps/p4est_step4 \ + example/steps/p4est_step5 example_steps_p4est_step1_SOURCES = example/steps/p4est_step1.c example_steps_p4est_step2_SOURCES = example/steps/p4est_step2.c example_steps_p4est_step3_SOURCES = example/steps/p4est_step3.c example_steps_p4est_step4_SOURCES = example/steps/p4est_step4.c - -LINT_CSOURCES += $(example_steps_p4est_step1_SOURCES) \ - $(example_steps_p4est_step2_SOURCES) \ - $(example_steps_p4est_step3_SOURCES) \ - $(example_steps_p4est_step4_SOURCES) +example_steps_p4est_step5_SOURCES = example/steps/p4est_step5.c endif if P4EST_ENABLE_BUILD_3D @@ -25,20 +22,17 @@ bin_PROGRAMS += \ example/steps/p8est_step1 \ example/steps/p8est_step2 \ example/steps/p8est_step3 \ - example/steps/p8est_step4 + example/steps/p8est_step4 \ + example/steps/p8est_step5 example_steps_p8est_step1_SOURCES = example/steps/p8est_step1.c example_steps_p8est_step2_SOURCES = example/steps/p8est_step2.c example_steps_p8est_step3_SOURCES = example/steps/p8est_step3.c example_steps_p8est_step4_SOURCES = example/steps/p8est_step4.c - -LINT_CSOURCES += $(example_steps_p8est_step1_SOURCES) \ - $(example_steps_p8est_step2_SOURCES) \ - $(example_steps_p8est_step3_SOURCES) \ - $(example_steps_p8est_step4_SOURCES) +example_steps_p8est_step5_SOURCES = example/steps/p8est_step5.c endif EXTRA_DIST += \ - example/steps/hw32.h + example/steps/hw32.h dist_p4estdata_DATA += \ example/steps/hole_2d_cubit.inp \ diff --git a/example/steps/hw32.h b/example/steps/hw32.h index f13fb69fc..8c0a53de4 100644 --- a/example/steps/hw32.h +++ b/example/steps/hw32.h @@ -2,7 +2,7 @@ /** \file hw32.h * - * This file containts the image data used in the step1 example. + * This file contains the image data used in the step1 example. */ #ifdef P4EST_ENABLE_DEBUG diff --git a/example/steps/p4est_step3.c b/example/steps/p4est_step3.c index 8fa31ee95..6042e5d10 100644 --- a/example/steps/p4est_step3.c +++ b/example/steps/p4est_step3.c @@ -42,16 +42,44 @@ #include #include #include +#include +#include #else #include #include #include #include +#include +#include #endif +#include + +/* The file format and API for checkpoint load/save will change */ +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +#ifndef P4_TO_P8 +#define P4EST_DATA_FILE_EXT "p4d" /**< file extension of p4est data files */ +#else +#define P4EST_DATA_FILE_EXT P8EST_DATA_FILE_EXT +#define P8EST_DATA_FILE_EXT "p8d" /**< file extension of p8est data files */ +#endif + +#define STEP3_BLOCK_SIZE (sizeof (step3_ctx_t)) /**< number of bytes of + the simulation context */ +#define STEP3_ENDIAN_CHECK 0x30415062 /* check endian; bPA0 in ASCII */ + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ /** We had 1. / 0. here to create a NaN but that is not portable. */ static const double step3_invalid = -1.; +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +/** A Boolean to decide if checkpoint files are written to disk. */ +static int step3_checkpoint = 0; + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + /* In this example we store data with each quadrant/octant. */ /** Per-quadrant data for this example. @@ -88,6 +116,9 @@ typedef struct step3_ctx between repartitioning */ int write_period; /**< the number of time steps between writing vtk files */ + double current_time; /**< the current time */ + int time_step; /**< current time step + counted from the first start. */ } step3_ctx_t; @@ -578,6 +609,240 @@ step3_write_solution (p4est_t * p4est, int timestep) sc_array_destroy (u_interp); } +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +/** Write a checkpoint file of the current simulation. + * The file can be loaded using \ref step3_restart to + * restart the simulation. + * The checkpoint file is not compiler independent since structures + * may be padded by the compiler (e. g., sizeof (step3_data_t)). + * This can result in different section sizes. + * + * \param [in] p4est the forest, whose quadrant data contains the state + * \param [in] timestep the timestep number, used to name the output files + */ +static void +step3_write_checkpoint (p4est_t * p4est, int timestep) +{ + char filename[BUFSIZ] = ""; + char user_string[P4EST_FILE_USER_STRING_BYTES] = ""; + char quad_data_user_string[P4EST_FILE_USER_STRING_BYTES] = + ""; + int errcode; + p4est_file_context_t *fc; + uint32_t check_endianness = STEP3_ENDIAN_CHECK; + sc_array_t block_arr; + + /** To write the data to the checkpoint file we need to store it + * in a linear array. Therefore, we first need to create such a linear + * array consisting of the quadrant data that is not stored in a linear + * array in this example code. In this case one could also use + * p4est_{load,save} to read and write the p4est including its + * quadrant data but we use the p4est_file functions for + * demonstration purposes. The p4est_file functions allow us to + * store less data than p4est_{load,save} and the p4est_file + * functions are required if an application uses external data + * storage, that is there is data that is not stored as quadrant + * data but for example in a linear array associated by its indexing + * to quadrants of a p4est. + */ + + snprintf (filename, BUFSIZ, + P4EST_STRING "_step3_checkpoint%04d." P4EST_DATA_FILE_EXT, + timestep); + + fc = p4est_file_open_create (p4est, filename, "Checkpoint file", &errcode); + /* One could use the error codes in \ref p4est_io.h and \ref p4est_file_error_string + * for more sophisticated error handling. + */ + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING "_file_open_create: Error creating file"); + + snprintf (user_string, P4EST_FILE_USER_STRING_BYTES, "%s", "Endianness"); + + sc_array_init_data (&block_arr, &check_endianness, sizeof (uint32_t), 1); + /* write data to check endianness */ + fc = + p4est_file_write_block (fc, sizeof (uint32_t), &block_arr, + user_string, &errcode); + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING "_file_write_block: Error writing endianness"); + + snprintf (user_string, P4EST_FILE_USER_STRING_BYTES, "%s", + "Simulation context"); + + sc_array_init_data (&block_arr, p4est->user_pointer, STEP3_BLOCK_SIZE, 1); + /* write the simulation context */ + fc = + p4est_file_write_block (fc, STEP3_BLOCK_SIZE, &block_arr, + user_string, &errcode); + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING + "_file_write_block: Error writing simulation context"); + + snprintf (user_string, P4EST_FILE_USER_STRING_BYTES, + P4EST_STRING " connecitivity"); + + /* write connectivity of the p4est */ + fc = + p4est_file_write_connectivity (fc, p4est->connectivity, user_string, + &errcode); + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING + "_file_write_connectivity: Error writing connectivity"); + + snprintf (user_string, P4EST_FILE_USER_STRING_BYTES, + "Quadrants of time step %04d.", timestep); + snprintf (quad_data_user_string, P4EST_FILE_USER_STRING_BYTES, + "Quadrant data of time step %04d.", timestep); + + /** Write the current p4est to the checkpoint file; we do not write the + * connectivity to disk because the connectivity is always the same in + * this example and can be created again for each restart. + */ + fc = p4est_file_write_p4est (fc, p4est, + user_string, quad_data_user_string, &errcode); + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING "_file_write_field: Error writing p4est"); + + p4est_file_close (fc, &errcode); + SC_CHECK_ABORT (errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING "_file_close: Error closing file"); +} + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + +static void step3_timestep (p4est_t * p4est, double start_time, + double end_time); + +static void step3_run (sc_MPI_Comm mpicomm); + +/** Load a checkpoint file to restart the simulation. + * The checkpoint file is not compiler independent since structures + * may be padded by the compiler (e. g., sizeof (step3_data_t)). + * This can result in different section sizes. + * + * \param [in] filename The file path to the checkpoint file + * created using \ref step3_write_checkpoint. + * \param [in] mpicomm The MPI communciatior that is used for + * the parallel simulation. + * \param [in] time_inc The time increment added to the current time + * of the restarted simulation to obtain the new + * end time. + */ +static void +step3_restart (const char *filename, sc_MPI_Comm mpicomm, double time_inc) +{ +#ifdef P4EST_ENABLE_FILE_DEPRECATED + int errcode; + char user_string[P4EST_FILE_USER_STRING_BYTES], + quad_string[P4EST_FILE_USER_STRING_BYTES], + quad_data_string[P4EST_FILE_USER_STRING_BYTES]; + step3_ctx_t ctx; + p4est_gloidx_t global_num_quadrants; + p4est_file_context_t *fc; + p4est_t *loaded_p4est; + p4est_connectivity_t *conn; + uint32_t check_endianness = STEP3_ENDIAN_CHECK; + int32_t read_check_endianness; + sc_array_t block_arr; +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + + if (filename == NULL) { + /* we do not restart */ + step3_run (mpicomm); + return; + } +#ifndef P4EST_ENABLE_FILE_DEPRECATED + else { + /** If the deprecated file format is not activated, the filename + * must be not set. + */ + SC_ABORT_NOT_REACHED (); + } +#else + /* we use file I/O to load a checkpoint. API will change */ + fc = + p4est_file_open_read_ext (mpicomm, filename, user_string, + &global_num_quadrants, &errcode); + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING "_file_open_read: Error opening file"); + + sc_array_init_data (&block_arr, &read_check_endianness, sizeof (uint32_t), + 1); + /* read data endianness */ + fc = + p4est_file_read_block (fc, sizeof (uint32_t), &block_arr, + user_string, &errcode); + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING + "_file_read_block: Error reading data endianness"); + P4EST_GLOBAL_PRODUCTIONF ("Read data with user string: %s\n", user_string); + + /* Instead of handling the wrong endianness, we just abort on it. + * In a more sophisticated application the data should be converted. + */ + SC_CHECK_ABORT (memcmp + (&read_check_endianness, &check_endianness, + sizeof (uint32_t)) == 0, "Wrong endianness"); + + sc_array_init_data (&block_arr, &ctx, STEP3_BLOCK_SIZE, 1); + /* read the simulation context */ + fc = + p4est_file_read_block (fc, STEP3_BLOCK_SIZE, &block_arr, user_string, + &errcode); + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING + "_file_read_block: Error reading simulation context"); + P4EST_GLOBAL_PRODUCTIONF ("Read data with user string: %s\n", user_string); + + /* read the connectivity */ + fc = p4est_file_read_connectivity (fc, &conn, user_string, &errcode); + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING + "_file_read_connectivity: Error reading connectivity"); + P4EST_ASSERT (conn != NULL); + P4EST_GLOBAL_PRODUCTIONF ("Read data with user string: %s\n", user_string); + + fc = p4est_file_read_p4est (fc, conn, + sizeof (step3_data_t), + &loaded_p4est, quad_string, quad_data_string, + &errcode); + SC_CHECK_ABORT (fc != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING "_file_read_field_ext: Error reading p4est"); + P4EST_GLOBAL_PRODUCTIONF ("Read quadrants with user string: %s\n", + quad_string); + P4EST_GLOBAL_PRODUCTIONF ("Read quadrant data with user string: %s\n", + quad_data_string); + + /* assign simulation context pointer */ + loaded_p4est->user_pointer = (void *) block_arr.array; + + /* close the file */ + p4est_file_close (fc, &errcode); + SC_CHECK_ABORT (errcode == P4EST_FILE_ERR_SUCCESS, + P4EST_STRING "_file_close: Error closing file"); + + step3_timestep (loaded_p4est, ctx.current_time, + ctx.current_time + time_inc); + + /* clean up */ + conn = loaded_p4est->connectivity; + p4est_destroy (loaded_p4est); + p4est_connectivity_destroy (conn); +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ +} + /** Approximate the divergence of (vu) on each quadrant * * We use piecewise constant approximations on each quadrant, so the value is @@ -992,12 +1257,13 @@ step3_get_timestep (p4est_t * p4est) * Update the state, refine, repartition, and write the solution to file. * * \param [in,out] p4est the forest, whose state is updated - * \param [in] time the end time + * \param [in] start_time simulation start time + * \param [in] end_time simulation end time */ static void -step3_timestep (p4est_t * p4est, double time) +step3_timestep (p4est_t * p4est, double start_time, double end_time) { - double t = 0.; + double t = start_time; double dt = 0.; int i; step3_data_t *ghost_data; @@ -1030,7 +1296,7 @@ step3_timestep (p4est_t * p4est, double time) #endif NULL); /* there is no callback for the corners between quadrants */ - for (t = 0., i = 0; t < time; t += dt, i++) { + for (t = start_time, i = ctx->time_step; t < end_time; t += dt, i++) { P4EST_GLOBAL_PRODUCTIONF ("time %f\n", t); /* refine */ @@ -1134,43 +1400,47 @@ step3_timestep (p4est_t * p4est, double time) NULL, /* there is no callback for the edges between quadrants */ #endif NULL); /* there is no callback for the corners between quadrants */ + } + ctx->time_step = i - 1; + ctx->current_time = t - dt; + +#ifdef P4EST_ENABLE_FILE_DEPRECATED + + if (step3_checkpoint) { + /* write checkpoint file */ + step3_write_checkpoint (p4est, i - 1); + } + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + P4EST_FREE (ghost_data); p4est_ghost_destroy (ghost); } -/** The main step 3 program. +/** Run the simulation and store the results as quadrant data. * * Setup of the example parameters; create the forest, with the state variable * stored in the quadrant data; refine, balance, and partition the forest; * timestep; clean up, and exit. - */ -int -main (int argc, char **argv) + * + * \param [in] mpicomm The MPI communicator that is used to + * create the corresponding simulation + * p4est. +*/ +static void +step3_run (sc_MPI_Comm mpicomm) { - int mpiret; - int recursive, partforcoarsen; - sc_MPI_Comm mpicomm; - p4est_t *p4est; p4est_connectivity_t *conn; + p4est_t *p4est; + int recursive, partforcoarsen; step3_ctx_t ctx; - /* Initialize MPI; see sc_mpi.h. - * If configure --enable-mpi is given these are true MPI calls. - * Else these are dummy functions that simulate a single-processor run. */ - mpiret = sc_MPI_Init (&argc, &argv); - SC_CHECK_MPI (mpiret); - mpicomm = sc_MPI_COMM_WORLD; - - /* These functions are optional. If called they store the MPI rank as a - * static variable so subsequent global p4est log messages are only issued - * from processor zero. Here we turn off most of the logging; see sc.h. */ - sc_init (mpicomm, 1, 1, NULL, SC_LP_ESSENTIAL); - p4est_init (NULL, SC_LP_PRODUCTION); - P4EST_GLOBAL_PRODUCTIONF - ("This is the p4est %dD demo example/steps/%s_step3\n", - P4EST_DIM, P4EST_STRING); + /* Avoid write of uninitialized bytes (valgrind warning) + * due to compiler padding. + */ + memset (&ctx, -1, sizeof (ctx)); ctx.bump_width = 0.1; ctx.max_err = 2.e-2; @@ -1191,6 +1461,7 @@ main (int argc, char **argv) ctx.refine_period = 2; ctx.repartition_period = 4; ctx.write_period = 8; + ctx.time_step = 0; /* Create a forest that consists of just one periodic quadtree/octree. */ #ifndef P4_TO_P8 @@ -1229,11 +1500,70 @@ main (int argc, char **argv) p4est_partition (p4est, partforcoarsen, NULL); /* time step */ - step3_timestep (p4est, 0.1); + step3_timestep (p4est, 0., 0.1); /* Destroy the p4est and the connectivity structure. */ p4est_destroy (p4est); p4est_connectivity_destroy (conn); +} + +/** The main step 3 program. + */ +int +main (int argc, char **argv) +{ + int mpiret, retval; + int help, usage_error; + sc_MPI_Comm mpicomm; + sc_options_t *opt; + const char *filename; + + /* Initialize MPI; see sc_mpi.h. + * If configure --enable-mpi is given these are true MPI calls. + * Else these are dummy functions that simulate a single-processor run. */ + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + mpicomm = sc_MPI_COMM_WORLD; + + /* These functions are optional. If called they store the MPI rank as a + * static variable so subsequent global p4est log messages are only issued + * from processor zero. Here we turn off most of the logging; see sc.h. */ + sc_init (mpicomm, 1, 1, NULL, SC_LP_ESSENTIAL); + p4est_init (NULL, SC_LP_PRODUCTION); + P4EST_GLOBAL_PRODUCTIONF + ("This is the p4est %dD demo example/steps/%s_step3\n", + P4EST_DIM, P4EST_STRING); + + /* Read command line options */ + filename = NULL; + opt = sc_options_new (argv[0]); + sc_options_add_bool (opt, 'H', "help", &help, 0, + "Print the help string and end the program"); +#ifdef P4EST_ENABLE_FILE_DEPRECATED + sc_options_add_bool (opt, 'C', "write-checkpoint", &step3_checkpoint, 0, + "Write checkpoint files to disk"); + sc_options_add_string (opt, 'L', "load-checkpoint", &filename, + NULL, "Load and start from a checkpoint file"); +#endif + retval = sc_options_parse (p4est_package_id, SC_LP_ERROR, opt, argc, argv); + usage_error = (retval == -1 || retval < argc); + + /* Action of the main program: error out or run demonstration */ + if (usage_error || help) { + sc_options_print_usage (p4est_package_id, SC_LP_PRODUCTION, opt, NULL); + } + else { + sc_options_print_summary (p4est_package_id, SC_LP_PRODUCTION, opt); + + /** Load a checkpoint file and restart the simulation for + * read filename != NULL. For filename == NULL the simulation + * is run from the beginning (t = 0). + */ + step3_restart (filename, mpicomm, 0.1); + } + + /* End of program -- free allocated memory */ + sc_options_destroy (opt); /* Verify that allocations internal to p4est and sc do not leak memory. * This should be called if sc_init () has been called earlier. */ @@ -1242,5 +1572,5 @@ main (int argc, char **argv) /* This is standard MPI programs. Without --enable-mpi, this is a dummy. */ mpiret = sc_MPI_Finalize (); SC_CHECK_MPI (mpiret); - return 0; + return usage_error ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/example/steps/p4est_step5.c b/example/steps/p4est_step5.c new file mode 100644 index 000000000..e5c8c0f31 --- /dev/null +++ b/example/steps/p4est_step5.c @@ -0,0 +1,673 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file p4est_step5.c + * + * This 2D example program illustrates high-order visualization. + * Contributed by: Grant Seastream, ExxonMobil + */ + +#ifndef P4_TO_P8 +#include +#include +#else +#include +#include +#endif + +/* change the following to #if 0 to revert to low-order visualization */ +#if 1 +#define STEP5_HO +#endif + +#define STEP5_NNODE_1D 8 +#ifndef P4_TO_P8 +#define STEP5_NNODE (STEP5_NNODE_1D * STEP5_NNODE_1D) +#else +#define STEP5_NNODE (STEP5_NNODE_1D * STEP5_NNODE_1D * STEP5_NNODE_1D) +#endif + +/** Per-quadrant data for this example. + * + * We store coordinates and a data value for visualization. + */ +typedef struct step5_data +{ + /* Element coordinates in the form of x_transpose = [ x_0 ... x_n, + * y_0 ... y_n, z_0 ... z_n] */ + double xt[P4EST_DIM * STEP5_NNODE]; + double data[STEP5_NNODE]; /* data values for points */ +} +step5_data_t; + +/* -------------------------------------------------------------------------- + * Retrieve info about GLL integration for specific number of points (in 1D) + * See the following link to generate x and w: + * https://www.mathworks.com/matlabcentral/fileexchange/ + * 4775-legende-gauss-lobatto-nodes-and-weights + * --------------------------------------------------------------------------*/ +static void +step5_get_GLL_info (double *x, double *w, const int num_points) +{ + switch (num_points) { + case 2: + x[0] = -1.0; + x[1] = +1.0; + + w[0] = 1.0; + w[1] = 1.0; + break; + + case 3: + x[0] = -1.0; + x[1] = 0.0; + x[2] = +1.0; + + w[0] = 0.333333333333333; + w[1] = 1.333333333333333; + w[2] = 0.333333333333333; + break; + + case 4: + x[0] = -1.0; + x[1] = -0.447213595499957; + x[2] = +0.447213595499957; + x[3] = +1.0; + + w[0] = 0.166666666666667; + w[1] = 0.833333333333333; + w[2] = 0.833333333333333; + w[3] = 0.166666666666667; + break; + + case 5: + x[0] = -1.0; + x[1] = -0.654653670707977; + x[2] = 0.0; + x[3] = +0.654653670707977; + x[4] = +1.0; + + w[0] = 0.1; + w[1] = 0.544444444444444; + w[2] = 0.711111111111111; + w[3] = 0.544444444444444; + w[4] = 0.1; + break; + + case 6: + x[0] = -1.0; + x[1] = -0.765055323929464; + x[2] = -0.285231516480645; + x[3] = +0.285231516480645; + x[4] = +0.765055323929464; + x[5] = +1.0; + + w[0] = 0.0666666666666667; + w[1] = 0.378474956297846; + w[2] = 0.554858377035486; + w[3] = 0.554858377035486; + w[4] = 0.378474956297846; + w[5] = 0.0666666666666667; + break; + + case 7: + x[0] = -1.0; + x[1] = -0.830223896278567; + x[2] = -0.468848793470714; + x[3] = 0.0; + x[4] = +0.468848793470714; + x[5] = +0.830223896278567; + x[6] = +1.0; + + w[0] = 0.047619047619048; + w[1] = 0.276826047361566; + w[2] = 0.431745381209863; + w[3] = 0.487619047619048; + w[4] = 0.431745381209863; + w[5] = 0.276826047361566; + w[6] = 0.047619047619048; + break; + + case 8: + x[0] = -1.0; + x[1] = -0.871740148509607; + x[2] = -0.591700181433142; + x[3] = -0.209299217902479; + x[4] = +0.209299217902479; + x[5] = +0.591700181433142; + x[6] = +0.871740148509607; + x[7] = +1.0; + + w[0] = 0.035714285714286; + w[1] = 0.210704227143506; + w[2] = 0.341122692483504; + w[3] = 0.412458794658704; + w[4] = 0.412458794658704; + w[5] = 0.341122692483504; + w[6] = 0.210704227143506; + w[7] = 0.035714285714286; + break; + } +} + +/* ---------------------------------------------------------------------------- + * convert p4est coordinate to physical coordinates for all 4 or 8 vertices (2D + * or 3D) + * --------------------------------------------------------------------------*/ +static void +step5_qcoord_to_vertex_all (const p4est_t * p4est, + const p4est_topidx_t which_tree, + const p4est_quadrant_t * q, double vxy_all[]) +{ + /* *INDENT-OFF* */ + /* Transform a quadrant coordinate into the space spanned by tree vertices. + see p4est vertex convention: + + 2----3 6----7 + | | / / + | | 4----5 + 0----1 | + | 2----3 + |/ / + 0----1 + */ + /* *INDENT-ON* */ +#ifndef P4_TO_P8 + /* each call will write to vxy_all[i], vxy_all[i+1], and vxy_all[i+2]. + * we'll let each next call overwrite the previous 3rd index since we don't + * need it. we pass a temporary array of 3 for the final call. */ + double vxy_temp[3]; + + /* p4est vertex 0 */ + p4est_qcoord_to_vertex (p4est->connectivity, which_tree, q->x, q->y, + &vxy_all[0]); + /* p4est vertex 1 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x + P4EST_QUADRANT_LEN (q->level), + q->y, &vxy_all[2]); + /* p4est vertex 2 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x, + q->y + P4EST_QUADRANT_LEN (q->level), &vxy_all[4]); + /* p4est vertex 3 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x + P4EST_QUADRANT_LEN (q->level), + q->y + P4EST_QUADRANT_LEN (q->level), vxy_temp); + vxy_all[6] = vxy_temp[0]; + vxy_all[7] = vxy_temp[1]; +#else + /* p4est vertex 0 */ + p4est_qcoord_to_vertex (p4est->connectivity, which_tree, q->x, q->y, q->z, + &vxy_all[0]); + /* p4est vertex 1 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x + P4EST_QUADRANT_LEN (q->level), + q->y, q->z, &vxy_all[3]); + /* p4est vertex 2 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x, + q->y + P4EST_QUADRANT_LEN (q->level), + q->z, &vxy_all[6]); + /* p4est vertex 3 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x + P4EST_QUADRANT_LEN (q->level), + q->y + P4EST_QUADRANT_LEN (q->level), + q->z, &vxy_all[9]); + /* p4est vertex 4 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x, + q->y, + q->z + P4EST_QUADRANT_LEN (q->level), &vxy_all[12]); + /* p4est vertex 5 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x + P4EST_QUADRANT_LEN (q->level), + q->y, + q->z + P4EST_QUADRANT_LEN (q->level), &vxy_all[15]); + /* p4est vertex 6 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x, + q->y + P4EST_QUADRANT_LEN (q->level), + q->z + P4EST_QUADRANT_LEN (q->level), &vxy_all[18]); + /* p4est vertex 7 */ + p4est_qcoord_to_vertex (p4est->connectivity, + which_tree, + q->x + P4EST_QUADRANT_LEN (q->level), + q->y + P4EST_QUADRANT_LEN (q->level), + q->z + P4EST_QUADRANT_LEN (q->level), &vxy_all[21]); +#endif +} + +#ifndef P4_TO_P8 +/* ---------------------------------------------------------------------------- + * 2D linear shape functions evaluated at given coordinates + * --------------------------------------------------------------------------*/ +static void +step5_shape_p1 (const double r, const double s, double *fn) +{ + fn[0] = 0.25 * (1.0 - r) * (1.0 - s); + fn[1] = 0.25 * (1.0 + r) * (1.0 - s); + fn[2] = 0.25 * (1.0 - r) * (1.0 + s); + fn[3] = 0.25 * (1.0 + r) * (1.0 + s); + +} +#else +/* ---------------------------------------------------------------------------- + * 3D linear shape functions evaluated at given coordinates + * --------------------------------------------------------------------------*/ +static void +step5_shape_p1 (const double r, const double s, const double t, double *fn) +{ + fn[0] = 0.125 * (1.0 - r) * (1.0 - s) * (1.0 - t); + fn[1] = 0.125 * (1.0 + r) * (1.0 - s) * (1.0 - t); + fn[2] = 0.125 * (1.0 - r) * (1.0 + s) * (1.0 - t); + fn[3] = 0.125 * (1.0 + r) * (1.0 + s) * (1.0 - t); + fn[4] = 0.125 * (1.0 - r) * (1.0 - s) * (1.0 + t); + fn[5] = 0.125 * (1.0 + r) * (1.0 - s) * (1.0 + t); + fn[6] = 0.125 * (1.0 - r) * (1.0 + s) * (1.0 + t); + fn[7] = 0.125 * (1.0 + r) * (1.0 + s) * (1.0 + t); + +} +#endif + +/* ---------------------------------------------------------------------------- + * construct GLL xyz for high-order elements + * --------------------------------------------------------------------------*/ +static void +step5_construct_GLL_xyz (const double vxy_all[], + const int xt_nrows_dim, + const int xt_ncols_nnode, double *xt) +{ + /* *INDENT-OFF* */ + /* construct GLL points for p2 elements given p4est physical coordinates - + see convention below. + + 2----3 6----7 ---- 3 ---- + | | / / / / + | | 4----5 0 1 + 0----1 | / / + | 2----3 ---- 2 ---- + |/ / + 0----1 + */ + /* *INDENT-ON* */ + + int i, j, ii, jj, visited_node, i_dim; + double xi, xj; +#ifdef P4_TO_P8 + int k; + double xk; +#endif + /* import vertex geometry corresponding to a p1 element: */ + double xt_p1[P4EST_DIM][P4EST_CHILDREN]; + /* compute geometry for high-order elements: */ + double xint[STEP5_NNODE_1D]; + double wint[STEP5_NNODE_1D]; + double fn[P4EST_CHILDREN]; /* array containing shape + * functions for 3D p1 + * elements */ + double xy_computed[P4EST_DIM]; + + step5_get_GLL_info (xint, wint, STEP5_NNODE_1D); + +#ifndef P4_TO_P8 + /* fill the corners */ + xt_p1[0][0] = vxy_all[0]; + xt_p1[1][0] = vxy_all[1]; + xt_p1[0][1] = vxy_all[2]; + xt_p1[1][1] = vxy_all[3]; + xt_p1[0][2] = vxy_all[4]; + xt_p1[1][2] = vxy_all[5]; + xt_p1[0][3] = vxy_all[6]; + xt_p1[1][3] = vxy_all[7]; + + for (j = 0; j < STEP5_NNODE_1D; ++j) { + /* y-direction in reference coordinate {s} */ + xj = xint[j]; + + for (i = 0; i < STEP5_NNODE_1D; ++i) { + /* x-direction in reference coordinate {r} */ + xi = xint[i]; + + /* p1 shape functions are evaluated at GLL points of the desired + * polynomial degree */ + step5_shape_p1 (xi, xj, fn); + + for (ii = 0; ii < P4EST_DIM; ++ii) { + xy_computed[ii] = 0.0; + for (jj = 0; jj < P4EST_CHILDREN; ++jj) { + xy_computed[ii] += xt_p1[ii][jj] * fn[jj]; + } + } + + /* construct geometry for a high-order element: + * find node that is being visited */ + visited_node = i + j * STEP5_NNODE_1D; + for (i_dim = 0; i_dim < xt_nrows_dim; i_dim++) { + xt[visited_node + i_dim * xt_ncols_nnode] = xy_computed[i_dim]; + } + } + } +#else + /* fill the corners */ + xt_p1[0][0] = vxy_all[0]; + xt_p1[1][0] = vxy_all[1]; + xt_p1[2][0] = vxy_all[2]; + xt_p1[0][1] = vxy_all[3]; + xt_p1[1][1] = vxy_all[4]; + xt_p1[2][1] = vxy_all[5]; + xt_p1[0][2] = vxy_all[6]; + xt_p1[1][2] = vxy_all[7]; + xt_p1[2][2] = vxy_all[8]; + xt_p1[0][3] = vxy_all[9]; + xt_p1[1][3] = vxy_all[10]; + xt_p1[2][3] = vxy_all[11]; + xt_p1[0][4] = vxy_all[12]; + xt_p1[1][4] = vxy_all[13]; + xt_p1[2][4] = vxy_all[14]; + xt_p1[0][5] = vxy_all[15]; + xt_p1[1][5] = vxy_all[16]; + xt_p1[2][5] = vxy_all[17]; + xt_p1[0][6] = vxy_all[18]; + xt_p1[1][6] = vxy_all[19]; + xt_p1[2][6] = vxy_all[20]; + xt_p1[0][7] = vxy_all[21]; + xt_p1[1][7] = vxy_all[22]; + xt_p1[2][7] = vxy_all[23]; + + for (k = 0; k < STEP5_NNODE_1D; ++k) { + /* z-direction in reference coordinate {t} */ + xk = xint[k]; + + for (j = 0; j < STEP5_NNODE_1D; ++j) { + /* y-direction in reference coordinate {s} */ + xj = xint[j]; + + for (i = 0; i < STEP5_NNODE_1D; ++i) { + /* x-direction in reference coordinate {r} */ + xi = xint[i]; + + /* p1 shape functions are evaluated at GLL points of the desired + * polynomial degree */ + step5_shape_p1 (xi, xj, xk, fn); + + for (ii = 0; ii < P4EST_DIM; ++ii) { + xy_computed[ii] = 0.0; + for (jj = 0; jj < P4EST_CHILDREN; ++jj) { + xy_computed[ii] += xt_p1[ii][jj] * fn[jj]; + } + } + + /* construct geometry for a high-order element: + * find node that is being visited */ + visited_node = i + j * STEP5_NNODE_1D + + k * STEP5_NNODE_1D * STEP5_NNODE_1D; + for (i_dim = 0; i_dim < xt_nrows_dim; i_dim++) { + xt[visited_node + i_dim * xt_ncols_nnode] = xy_computed[i_dim]; + } + } + } + } +#endif +} + +static void +step5_init_initial_condition (p4est_t * p4est, + p4est_topidx_t which_tree, p4est_quadrant_t * q) +{ + step5_data_t *data = (step5_data_t *) q->p.user_data; + double vxy_all[P4EST_DIM * P4EST_CHILDREN]; + int i; + double x, y; +#ifdef P4_TO_P8 + double z; +#endif + + /* generate coordinates */ + step5_qcoord_to_vertex_all (p4est, which_tree, q, vxy_all); + step5_construct_GLL_xyz (vxy_all, P4EST_DIM, STEP5_NNODE, data->xt); + + for (i = 0; i < STEP5_NNODE; ++i) { + x = 10 * data->xt[i]; + y = 10 * data->xt[STEP5_NNODE + i]; + data->data[i] = sin (x) + cos (y); +#ifdef P4_TO_P8 + z = 10 * data->xt[2 * STEP5_NNODE + i]; + data->data[i] += sqrt (z); +#endif + } +} + +static void +step5_collect_info (p4est_iter_volume_info_t * info, void *user_data) +{ + int n, idx; + double *this_o_ptr; +#ifndef STEP5_HO + int npoints = P4EST_CHILDREN; + int corner_list[npoints]; /* only need corners */ +#else + int npoints = STEP5_NNODE; + int ndatas = 1; /* 1 piece of data to extract */ + int d; + p4est_locidx_t numquads; +#endif + + /* we passed the array of values to fill as + * the user_data in the call to p4est_iterate */ + sc_array_t *output = (sc_array_t *) user_data; + p4est_t *p4est = info->p4est; + p4est_quadrant_t *q = info->quad; + p4est_topidx_t which_tree = info->treeid; + + /* this is the index of q *within its tree's numbering*. + * We want to convert its index for all the + * quadrants on this process, which we do below */ + p4est_locidx_t local_id = info->quadid; + p4est_tree_t *tree; + step5_data_t *data = (step5_data_t *) q->p.user_data; + + tree = p4est_tree_array_index (p4est->trees, which_tree); + local_id += tree->quadrants_offset; /* make relative to the MPI process */ + +#ifndef STEP5_HO +#ifndef P4_TO_P8 + corner_list[0] = 0; + corner_list[1] = STEP5_NNODE_1D - 1; + corner_list[2] = STEP5_NNODE_1D * (STEP5_NNODE_1D - 1); + corner_list[3] = STEP5_NNODE_1D * STEP5_NNODE_1D - 1; +#else + corner_list[0] = 0; + corner_list[1] = STEP5_NNODE_1D - 1; + corner_list[2] = STEP5_NNODE_1D * (STEP5_NNODE_1D - 1); + corner_list[3] = STEP5_NNODE_1D * STEP5_NNODE_1D - 1; + corner_list[4] = STEP5_NNODE_1D * STEP5_NNODE_1D * (STEP5_NNODE_1D - 1); + corner_list[5] = STEP5_NNODE_1D * STEP5_NNODE_1D * (STEP5_NNODE_1D - 1) + + STEP5_NNODE_1D - 1; + corner_list[6] = STEP5_NNODE_1D * STEP5_NNODE_1D * STEP5_NNODE_1D - 1 - + (STEP5_NNODE_1D - 1); + corner_list[7] = STEP5_NNODE_1D * STEP5_NNODE_1D * STEP5_NNODE_1D - 1; +#endif +#endif + + /* extracting data */ + for (n = 0; n < npoints; ++n) { + this_o_ptr = (double *) sc_array_index (output, n + npoints * local_id); +#ifndef STEP5_HO + idx = corner_list[n]; +#else + idx = n; +#endif + *this_o_ptr = data->data[idx]; + } + +#ifdef STEP5_HO + numquads = p4est->local_num_quadrants; + /* copying coordinates of each node + * ordering: [ x_0, y_0, z_0 ... x_n, y_n, z_n ] */ + for (n = 0; n < STEP5_NNODE; ++n) { + for (d = 0; d < P4EST_DIM; ++d) { + this_o_ptr = + (double *) sc_array_index (output, + n * P4EST_DIM + d + + STEP5_NNODE * ((local_id * P4EST_DIM) + + ndatas * numquads)); + *this_o_ptr = data->xt[n + d * STEP5_NNODE]; + } + } +#endif +} + +int +main (int argc, char **argv) +{ + int mpiret, retval; + int numquads, array_size; + int ndatas = 1; +#ifndef STEP5_HO + int npoints = P4EST_CHILDREN; + int vals_per_node = ndatas; +#else + int npoints = STEP5_NNODE; + int vals_per_node = ndatas + P4EST_DIM; + sc_array_t *positions; +#endif + sc_array_t *data, *output; + sc_MPI_Comm mpicomm; + p4est_t *p4est; + p4est_connectivity_t *conn; + p4est_vtk_context_t *vtk_context; + + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + mpicomm = sc_MPI_COMM_WORLD; + + sc_init (mpicomm, 1, 1, NULL, SC_LP_ESSENTIAL); + p4est_init (NULL, SC_LP_PRODUCTION); + P4EST_GLOBAL_PRODUCTIONF + ("This is the p4est %dD demo example/steps/%s_step5\n", + P4EST_DIM, P4EST_STRING); + +#ifndef P4_TO_P8 + conn = p4est_connectivity_new_unitsquare (); +#else + conn = p8est_connectivity_new_unitcube (); +#endif + + /* 4 cells in 2x2 square (in 2D), each point with coords and data values */ + p4est = p4est_new_ext (mpicomm, /* communicator */ + conn, /* connectivity */ + 0, /* minimum quadrants per MPI process */ + 1, /* minimum level of refinement */ + 1, /* fill uniform */ + sizeof (step5_data_t), /* data size */ + step5_init_initial_condition, /* initializes data */ + NULL); /* context */ + + numquads = p4est->local_num_quadrants; + + output = sc_array_new_count (sizeof (double), + numquads * vals_per_node * npoints); + + /* *INDENT-OFF* */ +#ifndef P4_TO_P8 + p4est_iterate(p4est, + NULL, + (void *)output, + step5_collect_info, + NULL, + NULL); +#else + p4est_iterate(p4est, + NULL, + (void *)output, + step5_collect_info, + NULL, + NULL, + NULL); +#endif + /* *INDENT-ON* */ + + array_size = numquads * npoints; + + /* begin writing the output files */ + vtk_context = p4est_vtk_context_new (p4est, P4EST_STRING "_step5"); + +#ifdef STEP5_HO + positions = sc_array_new_size (sizeof (double), P4EST_DIM * array_size); + sc_array_move_part (positions, + 0, + output, + (size_t) (ndatas * array_size), + (size_t) (P4EST_DIM * array_size)); + + /* In this example we do not shrink down each individual quadrant */ + p4est_vtk_context_set_scale (vtk_context, 1.); + + /* The continuous setting only effects the low-order output */ + p4est_vtk_context_set_continuous (vtk_context, 1); + + vtk_context = p4est_vtk_write_header_ho (vtk_context, + positions, STEP5_NNODE_1D); +#else + vtk_context = p4est_vtk_write_header (vtk_context); +#endif + SC_CHECK_ABORT (vtk_context != NULL, + P4EST_STRING "_vtk: Error writing header"); + + data = sc_array_new_size (sizeof (double), array_size); + sc_array_move_part (data, 0, output, 0, (size_t) array_size); + + vtk_context = p4est_vtk_write_point_dataf (vtk_context, + 1, 0, "data", data, vtk_context); + SC_CHECK_ABORT (vtk_context != NULL, + P4EST_STRING "_vtk: Error writing point data"); + + /* write_footer also calls vtk_context_destroy */ + retval = p4est_vtk_write_footer (vtk_context); + SC_CHECK_ABORT (!retval, P4EST_STRING "_vtk: Error writing footer"); + +#ifdef STEP5_HO + sc_array_destroy (positions); +#endif + sc_array_destroy (output); + sc_array_destroy (data); + + p4est_destroy (p4est); + p4est_connectivity_destroy (conn); + + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + return 0; +} diff --git a/example/steps/p8est_step5.c b/example/steps/p8est_step5.c new file mode 100644 index 000000000..b1c575ce4 --- /dev/null +++ b/example/steps/p8est_step5.c @@ -0,0 +1,32 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file p8est_step5.c + * + * This 3D example program illustrates high-order visualization. + * Contributed by: Grant Seastream, ExxonMobil + */ + +#include +#include "p4est_step5.c" diff --git a/example/tetgen/Makefile.am b/example/tetgen/Makefile.am index dca21d8bd..13f627a63 100644 --- a/example/tetgen/Makefile.am +++ b/example/tetgen/Makefile.am @@ -12,9 +12,6 @@ bin_PROGRAMS += \ example/tetgen/p4est_write_conn example/tetgen/p4est_read_conn example_tetgen_p4est_write_conn_SOURCES = example/tetgen/write_conn2.c example_tetgen_p4est_read_conn_SOURCES = example/tetgen/read_conn2.c - -LINT_CSOURCES += $(example_tetgen_p4est_write_conn_SOURCES) \ - $(example_tetgen_p4est_read_conn_SOURCES) endif if P4EST_ENABLE_BUILD_3D @@ -24,8 +21,4 @@ bin_PROGRAMS += \ example_tetgen_p8est_write_conn_SOURCES = example/tetgen/write_conn3.c example_tetgen_p8est_read_conn_SOURCES = example/tetgen/read_conn3.c example_tetgen_p8est_read_tetgen_SOURCES = example/tetgen/read_tetgen.c - -LINT_CSOURCES += $(example_tetgen_p8est_write_conn_SOURCES) \ - $(example_tetgen_p8est_read_conn_SOURCES) \ - $(example_tetgen_p8est_read_tetgen_SOURCES) endif diff --git a/example/timings/Makefile.am b/example/timings/Makefile.am index 2bf9976c6..c8952fff9 100644 --- a/example/timings/Makefile.am +++ b/example/timings/Makefile.am @@ -12,11 +12,6 @@ bin_PROGRAMS += \ example_timings_p4est_timings_SOURCES = example/timings/timings2.c example_timings_p4est_bricks_SOURCES = example/timings/bricks2.c example_timings_p4est_loadconn_SOURCES = example/timings/loadconn2.c - -LINT_CSOURCES += \ - $(example_timings_p4est_timings_SOURCES) \ - $(example_timings_p4est_bricks_SOURCES) \ - $(example_timings_p4est_loadconn_SOURCES) endif if P4EST_ENABLE_BUILD_3D @@ -30,12 +25,6 @@ example_timings_p8est_timings_SOURCES = example/timings/timings3.c example_timings_p8est_bricks_SOURCES = example/timings/bricks3.c example_timings_p8est_loadconn_SOURCES = example/timings/loadconn3.c example_timings_p8est_tsearch_SOURCES = example/timings/tsearch3.c - -LINT_CSOURCES += \ - $(example_timings_p8est_timings_SOURCES) \ - $(example_timings_p8est_bricks_SOURCES) \ - $(example_timings_p8est_loadconn_SOURCES) \ - $(example_timings_p8est_tsearch_SOURCES) endif EXTRA_DIST += example/timings/timana.awk example/timings/timana.sh diff --git a/sc b/sc index 81a6ddc33..d95bc1488 160000 --- a/sc +++ b/sc @@ -1 +1 @@ -Subproject commit 81a6ddc33408f1975581c09472fa86ce268740f0 +Subproject commit d95bc14889bf46b03e57dc1a7482e0754adbd4c8 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 91fb2dae9..97640e948 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,15 +1,19 @@ -target_sources(p4est PRIVATE p4est_base.c p4est_connectivity.c p4est.c p4est_bits.c p4est_search.c p4est_build.c p4est_algorithms.c p4est_communication.c p4est_ghost.c p4est_nodes.c p4est_points.c p4est_geometry.c p4est_iterate.c p4est_lnodes.c p4est_mesh.c p4est_balance.c p4est_io.c p4est_connrefine.c p4est_wrap.c p4est_plex.c p4est_empty.c) -if(P4EST_HAVE_WINSOCK2_H) - target_link_libraries(p4est PRIVATE ${WINSOCK_LIBRARIES}) -endif() -if(vtk) - target_sources(p4est PRIVATE p4est_vtk.c) -endif() +target_sources(p4est PRIVATE p4est_base.c p4est_connectivity.c p4est.c p4est_bits.c p4est_search.c p4est_build.c +p4est_algorithms.c p4est_communication.c p4est_ghost.c p4est_nodes.c p4est_points.c p4est_geometry.c p4est_iterate.c +p4est_lnodes.c p4est_mesh.c p4est_balance.c p4est_io.c p4est_connrefine.c +p4est_wrap.c p4est_plex.c p4est_empty.c p4est_vtk.c +) if(enable_p8est) -target_sources(p8est PRIVATE p8est_connectivity.c p8est.c p8est_bits.c p8est_search.c p8est_build.c p8est_algorithms.c p8est_communication.c p8est_ghost.c p8est_nodes.c p8est_vtk.c p8est_points.c p8est_geometry.c p8est_iterate.c p8est_lnodes.c p8est_mesh.c p8est_tets_hexes.c p8est_balance.c p8est_io.c p8est_connrefine.c p8est_wrap.c p8est_plex.c p8est_empty.c) + target_sources(p8est PRIVATE p8est_connectivity.c p8est.c p8est_bits.c p8est_search.c p8est_build.c + p8est_algorithms.c p8est_communication.c p8est_ghost.c p8est_nodes.c p8est_vtk.c p8est_points.c p8est_geometry.c + p8est_iterate.c p8est_lnodes.c p8est_mesh.c p8est_tets_hexes.c p8est_balance.c p8est_io.c p8est_connrefine.c + p8est_wrap.c p8est_plex.c p8est_empty.c p8est_vtk.c + ) endif(enable_p8est) if(enable_p6est AND enable_p8est) -target_sources(p6est PRIVATE p6est.c p6est_ghost.c p6est_lnodes.c p6est_profile.c p6est_vtk.c p6est_communication.c p6est_empty.c) + target_sources(p6est PRIVATE p6est.c p6est_ghost.c p6est_lnodes.c p6est_profile.c p6est_vtk.c + p6est_communication.c p6est_empty.c + ) endif() diff --git a/src/Makefile.am b/src/Makefile.am index 0d96429a9..f29ab5128 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -74,20 +74,26 @@ endif # this variable is used for headers that are not publicly installed P4EST_CPPFLAGS = +# read the .so version from configuration file +include config/p4est_soversion.in + justlibs-local: src/libp4est.la lib_LTLIBRARIES += src/libp4est.la src_libp4est_la_SOURCES = \ $(libp4est_internal_headers) \ $(libp4est_compiled_sources) src_libp4est_la_CPPFLAGS = $(AM_CPPFLAGS) $(P4EST_CPPFLAGS) -## src_libp4est_la_LDFLAGS = -release $(VERSION) -src_libp4est_la_LIBADD = @P4EST_SC_LDADD@ -LDADD += @top_builddir@/src/libp4est.la @P4EST_SC_LDADD@ -EXTRA_src_libp4est_la_DEPENDENCIES = @P4EST_SC_LDADD@ +## This is the official API versioning scheme of libtool. Please see: +## Read https://www.gnu.org/software/libtool/manual/libtool.html#Versioning +src_libp4est_la_LDFLAGS = -version-info $(P4EST_SOVERSION) +src_libp4est_la_LIBADD = @P4EST_SC_LIBADD@ +EXTRA_src_libp4est_la_DEPENDENCIES = @P4EST_SC_EDEPS@ + +## Code libsc library installation path into p4est executables +AM_LDFLAGS += @P4EST_SC_RPATH@ +LDADD += src/libp4est.la @P4EST_SC_LDADD@ nodist_include_HEADERS += $(libp4est_generated_headers) include_HEADERS += $(libp4est_installed_headers) AM_CPPFLAGS += -I@top_srcdir@/src @P4EST_SC_CPPFLAGS@ - -LINT_CSOURCES += $(libp4est_compiled_sources) diff --git a/src/p4est.c b/src/p4est.c index 6c56d8a2a..7eea8d2ab 100644 --- a/src/p4est.c +++ b/src/p4est.c @@ -247,7 +247,7 @@ p4est_new_ext (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, else { p4est->user_data_pool = NULL; } - p4est->quadrant_pool = sc_mempool_new (sizeof (p4est_quadrant_t)); + p4est->quadrant_pool = p4est_quadrant_mempool_new (); /* determine uniform level of initial tree */ tree_num_quadrants = 1; @@ -370,8 +370,7 @@ p4est_new_ext (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, (jt == first_tree && first_tree_quadrant == tree_num_quadrants - 1)) { /* There is only a in the tree */ - quad = p4est_quadrant_array_push (tquadrants); - *quad = a; + quad = p4est_quadrant_array_push_copy (tquadrants, &a); p4est_quadrant_init_data (p4est, jt, quad, init_fn); tree->maxlevel = a.level; tree->quadrants_per_level[a.level] = 1; @@ -417,10 +416,12 @@ p4est_new_ext (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, /* populate quadrant array in Morton order */ sc_array_resize (tquadrants, (size_t) count); quad = p4est_quadrant_array_index (tquadrants, 0); + P4EST_QUADRANT_INIT (quad); p4est_quadrant_set_morton (quad, level, first_morton); p4est_quadrant_init_data (p4est, jt, quad, init_fn); for (miu = 1; miu < count; ++miu) { quad = p4est_quadrant_array_index (tquadrants, (size_t) miu); + P4EST_QUADRANT_INIT (quad); p4est_quadrant_successor (quad - 1, quad); p4est_quadrant_init_data (p4est, jt, quad, init_fn); } @@ -576,7 +577,7 @@ p4est_copy_ext (p4est_t * input, int copy_data, int duplicate_mpicomm) else { p4est->data_size = 0; } - p4est->quadrant_pool = sc_mempool_new (sizeof (p4est_quadrant_t)); + p4est->quadrant_pool = p4est_quadrant_mempool_new (); /* copy quadrants for each tree */ p4est->trees = sc_array_new (sizeof (p4est_tree_t)); @@ -1199,8 +1200,7 @@ p4est_balance_schedule (p4est_t * p4est, p4est_balance_peer_t * peers, } /* copy quadrant into shipping list */ - s = p4est_quadrant_array_push (&peer->send_first); - *s = *q; + s = p4est_quadrant_array_push_copy (&peer->send_first, q); s->p.piggy2.which_tree = qtree; /* piggy back tree id */ /* update lowest and highest peer */ @@ -1496,8 +1496,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, } if (qarray != NULL) { - s = (p4est_quadrant_t *) sc_array_push (qarray); - *s = *q; + (void) p4est_quadrant_array_push_copy (qarray, q); } #ifdef P4_TO_P8 @@ -1722,7 +1721,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, max_ranges, my_ranges, &all_ranges); twomaxwin = 2 * maxwin; if (p4est->inspect != NULL) { - p4est->inspect->balance_ranges += MPI_Wtime (); + p4est->inspect->balance_ranges += sc_MPI_Wtime (); } sc_ranges_decode (num_procs, rank, maxwin, all_ranges, &num_receivers_ranges, receiver_ranks_ranges, @@ -1833,7 +1832,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, p4est->mpicomm); SC_CHECK_MPI (mpiret); if (p4est->inspect != NULL) { - p4est->inspect->balance_notify += MPI_Wtime (); + p4est->inspect->balance_notify += sc_MPI_Wtime (); } /* double-check sc_notify results by sc_notify_allgather */ @@ -1850,7 +1849,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, p4est->mpicomm); SC_CHECK_MPI (mpiret); if (p4est->inspect != NULL) { - p4est->inspect->balance_notify_allgather += MPI_Wtime (); + p4est->inspect->balance_notify_allgather += sc_MPI_Wtime (); } /* run verification against sc_notify_allgather */ @@ -2008,8 +2007,8 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, /* wait for quadrant counts and post receive and send for quadrants */ while (request_first_count > 0) { - mpiret = MPI_Waitsome (num_procs, requests_first, - &outcount, wait_indices, recv_statuses); + mpiret = sc_MPI_Waitsome (num_procs, requests_first, + &outcount, wait_indices, recv_statuses); SC_CHECK_MPI (mpiret); P4EST_ASSERT (outcount != MPI_UNDEFINED); P4EST_ASSERT (outcount > 0); @@ -2028,7 +2027,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, if (!peer->have_first_count) { /* verify message size */ P4EST_ASSERT (jstatus->MPI_TAG == P4EST_COMM_BALANCE_FIRST_COUNT); - mpiret = MPI_Get_count (jstatus, MPI_INT, &rcount); + mpiret = sc_MPI_Get_count (jstatus, MPI_INT, &rcount); SC_CHECK_MPI (mpiret); SC_CHECK_ABORTF (rcount == 1, "Receive count mismatch A %d", rcount); @@ -2062,7 +2061,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, /* verify received size */ P4EST_ASSERT (jstatus->MPI_TAG == P4EST_COMM_BALANCE_FIRST_LOAD); P4EST_ASSERT (peer->recv_first_count > 0); - mpiret = MPI_Get_count (jstatus, MPI_BYTE, &rcount); + mpiret = sc_MPI_Get_count (jstatus, MPI_BYTE, &rcount); SC_CHECK_MPI (mpiret); SC_CHECK_ABORTF (rcount == peer->recv_first_count * @@ -2145,8 +2144,8 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, #ifdef P4EST_ENABLE_MPI /* receive second round appending to the same receive buffer */ while (request_second_count > 0) { - mpiret = MPI_Waitsome (num_procs, requests_second, - &outcount, wait_indices, recv_statuses); + mpiret = sc_MPI_Waitsome (num_procs, requests_second, + &outcount, wait_indices, recv_statuses); SC_CHECK_MPI (mpiret); P4EST_ASSERT (outcount != MPI_UNDEFINED); P4EST_ASSERT (outcount > 0); @@ -2165,7 +2164,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, if (!peer->have_second_count) { /* verify message size */ P4EST_ASSERT (jstatus->MPI_TAG == P4EST_COMM_BALANCE_SECOND_COUNT); - mpiret = MPI_Get_count (jstatus, MPI_INT, &rcount); + mpiret = sc_MPI_Get_count (jstatus, MPI_INT, &rcount); SC_CHECK_MPI (mpiret); SC_CHECK_ABORTF (rcount == 1, "Receive count mismatch B %d", rcount); @@ -2199,7 +2198,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, /* verify received size */ P4EST_ASSERT (jstatus->MPI_TAG == P4EST_COMM_BALANCE_SECOND_LOAD); P4EST_ASSERT (peer->recv_second_count > 0); - mpiret = MPI_Get_count (jstatus, MPI_BYTE, &rcount); + mpiret = sc_MPI_Get_count (jstatus, MPI_BYTE, &rcount); SC_CHECK_MPI (mpiret); SC_CHECK_ABORTF (rcount == peer->recv_second_count * @@ -2291,8 +2290,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, } if (borders == NULL) { tree = p4est_tree_array_index (p4est->trees, qtree); - q = p4est_quadrant_array_push (&tree->quadrants); - *q = *s; + q = p4est_quadrant_array_push_copy (&tree->quadrants, s); ++tree->quadrants_per_level[q->level]; tree->maxlevel = (int8_t) SC_MAX (tree->maxlevel, q->level); ++p4est->local_num_quadrants; @@ -2301,8 +2299,7 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, else { qarray = (sc_array_t *) sc_array_index (borders, (int) (qtree - first_tree)); - q = p4est_quadrant_array_push (qarray); - *q = *s; + (void) p4est_quadrant_array_push_copy (qarray, s); } } } @@ -2343,8 +2340,8 @@ p4est_balance_ext (p4est_t * p4est, p4est_connect_type_t btype, #ifdef P4EST_ENABLE_MPI /* wait for all send operations */ if (request_send_count > 0) { - mpiret = MPI_Waitall (4 * num_procs, - send_requests_first_count, MPI_STATUSES_IGNORE); + mpiret = sc_MPI_Waitall (4 * num_procs, + send_requests_first_count, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); } @@ -2482,7 +2479,7 @@ p4est_partition_ext (p4est_t * p4est, int partition_for_coarsening, if (p4est->mpisize == 1) { P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_partition no shipping\n"); - /* in particular, there is no need to bumb the revision counter */ + /* in particular, there is no need to bump the revision counter */ P4EST_ASSERT (global_shipped == 0); return global_shipped; } @@ -2566,7 +2563,7 @@ p4est_partition_ext (p4est_t * p4est, int partition_for_coarsening, P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_partition no shipping\n"); - /* in particular, there is no need to bumb the revision counter */ + /* in particular, there is no need to bump the revision counter */ P4EST_ASSERT (global_shipped == 0); return global_shipped; } @@ -2695,19 +2692,20 @@ p4est_partition_ext (p4est_t * p4est, int partition_for_coarsening, /* wait for sends and receives to complete */ if (num_sends > 0) { - mpiret = MPI_Waitall (num_sends, send_requests, MPI_STATUSES_IGNORE); + mpiret = sc_MPI_Waitall (num_sends, send_requests, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); P4EST_FREE (send_requests); P4EST_FREE (send_array); } - mpiret = MPI_Waitall (2, recv_requests, recv_statuses); + mpiret = sc_MPI_Waitall (2, recv_requests, recv_statuses); SC_CHECK_MPI (mpiret); if (my_lowcut != 0) { SC_CHECK_ABORT (recv_statuses[0].MPI_SOURCE == low_source, "Wait low source"); SC_CHECK_ABORT (recv_statuses[0].MPI_TAG == P4EST_COMM_PARTITION_WEIGHTED_LOW, "Wait low tag"); - mpiret = MPI_Get_count (&recv_statuses[0], P4EST_MPI_GLOIDX, &rcount); + mpiret = + sc_MPI_Get_count (&recv_statuses[0], P4EST_MPI_GLOIDX, &rcount); SC_CHECK_MPI (mpiret); SC_CHECK_ABORTF (rcount == 1, "Wait low count %d", rcount); } @@ -2716,7 +2714,8 @@ p4est_partition_ext (p4est_t * p4est, int partition_for_coarsening, "Wait high source"); SC_CHECK_ABORT (recv_statuses[1].MPI_TAG == P4EST_COMM_PARTITION_WEIGHTED_HIGH, "Wait high tag"); - mpiret = MPI_Get_count (&recv_statuses[1], P4EST_MPI_GLOIDX, &rcount); + mpiret = + sc_MPI_Get_count (&recv_statuses[1], P4EST_MPI_GLOIDX, &rcount); SC_CHECK_MPI (mpiret); SC_CHECK_ABORTF (rcount == 1, "Wait high count %d", rcount); } @@ -2922,6 +2921,8 @@ p4est_partition_for_coarsening (p4est_t * p4est, P4EST_ASSERT (num_sends == old_num_sends); #endif + send_requests = NULL; + parent_send = NULL; if (num_sends > 0) { /* if this process sends messages */ /* allocate send messages */ send_requests = P4EST_ALLOC (MPI_Request, num_sends); @@ -2930,6 +2931,11 @@ p4est_partition_for_coarsening (p4est_t * p4est, /* array index of send messages */ parent_index = 0; + /* make memory valgrind clean */ + for (i = 0; i < num_sends; i++) { + P4EST_QUADRANT_INIT (&parent_send[i]); + } + for (i = send_lowest; i <= send_highest; i++) { /* loop over all process candidates to send to */ if (!(partition_new[i] < partition_new[i + 1] && @@ -2940,9 +2946,6 @@ p4est_partition_for_coarsening (p4est_t * p4est, continue; } - /* we have identified the next quadrant to be sent */ - p4est_quadrant_pad (parent_send + parent_index); - /* get nearest quadrant `quad_id_near_cut` to cut `partition_new[i]` */ if (partition_now[rank] <= partition_new[i] && partition_new[i] < partition_now[rank + 1]) { @@ -3264,13 +3267,13 @@ p4est_partition_for_coarsening (p4est_t * p4est, if (num_receives > 0) { /* wait for receives to complete */ mpiret = - MPI_Waitall (num_receives, receive_requests, MPI_STATUSES_IGNORE); + sc_MPI_Waitall (num_receives, receive_requests, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); /* free receive memory */ P4EST_FREE (receive_requests); } - /* END: wait for MPI recieve to complete */ + /* END: wait for MPI receive to complete */ /* BEGIN: compute correction with received quadrants */ if (num_receives > 0) { @@ -3333,7 +3336,7 @@ p4est_partition_for_coarsening (p4est_t * p4est, /* BEGIN: wait for MPI send to complete */ if (num_sends > 0) { /* wait for sends to complete */ - mpiret = MPI_Waitall (num_sends, send_requests, MPI_STATUSES_IGNORE); + mpiret = sc_MPI_Waitall (num_sends, send_requests, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); /* free send memory */ @@ -3385,7 +3388,7 @@ p4est_partition_for_coarsening (p4est_t * p4est, #ifdef P4EST_HAVE_ZLIB static void -p4est_checksum_local (p4est_t * p4est, uLong * local_crc, size_t * ssum, +p4est_checksum_local (p4est_t *p4est, uLong *local_crc, size_t *ssum, int partition_dependent) { uLong treecrc; @@ -3474,12 +3477,12 @@ p4est_save_ext (const char *filename, p4est_t * p4est, size_t data_size, qbuf_size, comb_size, head_count; size_t zz, zcount; uint64_t *u64a; - FILE *file; #ifdef P4EST_MPIIO_WRITE MPI_File mpifile; MPI_Offset mpipos; MPI_Offset mpithis; #else + FILE *file; long fthis; #endif p4est_topidx_t jt, num_trees; @@ -3571,9 +3574,6 @@ p4est_save_ext (const char *filename, p4est_t * p4est, /* file is still open for sequential write mode */ #endif } - else { - file = NULL; - } P4EST_FREE (pertree); #ifndef P4EST_MPIIO_WRITE @@ -3839,9 +3839,8 @@ p4est_load_mpi (const char *filename, sc_MPI_Comm mpicomm, size_t data_size, SC_CHECK_ABORT (!retval, "seek over ignored partition"); retval = sc_io_source_read (src, &u64int, sizeof (uint64_t), NULL); SC_CHECK_ABORT (!retval, "read quadrant count"); - for (i = 1; i <= num_procs; ++i) { - gfq[i] = p4est_partition_cut_uint64 (u64int, i, num_procs); - } + p4est_comm_global_first_quadrant ((p4est_gloidx_t) u64int, num_procs, + gfq); } } if (broadcasthead) { @@ -4150,9 +4149,8 @@ p4est_source_ext (sc_io_source_t * src, sc_MPI_Comm mpicomm, size_t data_size, SC_CHECK_ABORT (!retval, "seek over ignored partition"); retval = sc_io_source_read (src, &u64int, sizeof (uint64_t), NULL); SC_CHECK_ABORT (!retval, "read quadrant count"); - for (i = 1; i <= num_procs; ++i) { - gfq[i] = p4est_partition_cut_uint64 (u64int, i, num_procs); - } + p4est_comm_global_first_quadrant ((p4est_gloidx_t) u64int, num_procs, + gfq); } } if (broadcasthead) { diff --git a/src/p4est.h b/src/p4est.h index a7a93aa2a..d44bdfae5 100644 --- a/src/p4est.h +++ b/src/p4est.h @@ -388,12 +388,12 @@ void p4est_partition (p4est_t * p4est, /** Compute the checksum for a forest. * Based on quadrant arrays only. It is independent of partition and mpisize. - * \return Returns the checksum on processor 0 only. 0 on other processors. + * \return Returns the checksum on all processors. */ unsigned p4est_checksum (p4est_t * p4est); /** Compute a partition-dependent checksum for a forest. - * \return Returns the checksum on processor 0 only. 0 on other processors. + * \return Returns the checksum on all processors. */ unsigned p4est_checksum_partition (p4est_t * p4est); @@ -481,14 +481,44 @@ p4est_quadrant_array_index (sc_array_t * array, size_t it) return (p4est_quadrant_t *) (array->array + sizeof (p4est_quadrant_t) * it); } -/** Call sc_array_push for a quadrant array. */ +/** Push the copy of a fully initialized quadrant onto a quadrant array. + * \param [in,out] array Valid array of quadrants is pushed to. + * \param [in] qsrc Pointer to a quadrant with fully initialized memory. + * This means all bits in the quadrant mush have been + * written to at least once, even the compiler padding. + * This serves to make the function clean for valgrind. + * \return Newly allocated quadrant with contents of \a qsrc. + */ +static inline p4est_quadrant_t * +p4est_quadrant_array_push_copy (sc_array_t * array, + const p4est_quadrant_t *qsrc) +{ + p4est_quadrant_t *q; + + P4EST_ASSERT (array->elem_size == sizeof (p4est_quadrant_t)); + + q = (p4est_quadrant_t *) sc_array_push (array); + *q = *qsrc; + return q; +} + +/** Call sc_array_push for a quadrant array and fully initialize memory. + * \param [in,out] array Valid array of quadrants is pushed to. + * \return Pushed array element with fully initialized memory. + * In this case, we're writing to all bits of it. + * This serves to make the quadrant clean for valgrind. + */ /*@unused@*/ static inline p4est_quadrant_t * p4est_quadrant_array_push (sc_array_t * array) { + p4est_quadrant_t *q; + P4EST_ASSERT (array->elem_size == sizeof (p4est_quadrant_t)); - return (p4est_quadrant_t *) sc_array_push (array); + q = (p4est_quadrant_t *) sc_array_push (array); + P4EST_QUADRANT_INIT(q); + return q; } /** Call sc_mempool_alloc for a mempool creating quadrants. */ diff --git a/src/p4est_algorithms.c b/src/p4est_algorithms.c index 7e22a1a78..ea136303d 100644 --- a/src/p4est_algorithms.c +++ b/src/p4est_algorithms.c @@ -79,6 +79,12 @@ static const int pbco = P4EST_FACES; #endif /* !P4_TO_P8 */ +sc_mempool_t * +p4est_quadrant_mempool_new (void) +{ + return sc_mempool_new_zero_and_persist (sizeof (p4est_quadrant_t)); +} + void p4est_quadrant_init_data (p4est_t * p4est, p4est_topidx_t which_tree, p4est_quadrant_t * quad, p4est_init_t init_fn) @@ -793,7 +799,6 @@ p4est_output_array_push_data (sc_array_t * out, const p4est_quadrant_t * src, { p4est_quadrant_t *outq = p4est_quadrant_array_push (out); - p4est_quadrant_pad (outq); p4est_quadrant_sibling (src, outq, 0); outq->p.piggy2.which_tree = which_tree; /* *INDENT-OFF* HORRIBLE indent bug */ @@ -1399,8 +1404,7 @@ p4est_complete_region (p4est_t * p4est, /* R <- R + a */ if (include_q1) { - r = p4est_quadrant_array_push (quadrants); - *r = a; + r = p4est_quadrant_array_push_copy (quadrants, &a); p4est_quadrant_init_data (p4est, which_tree, r, init_fn); maxlevel = SC_MAX ((int) r->level, maxlevel); ++quadrants_per_level[r->level]; @@ -1446,8 +1450,7 @@ p4est_complete_region (p4est_t * p4est, ) && !p4est_quadrant_is_ancestor (w, &b) ) { /* R <- R + w */ - r = p4est_quadrant_array_push (quadrants); - *r = *w; + r = p4est_quadrant_array_push_copy (quadrants, w); p4est_quadrant_init_data (p4est, which_tree, r, init_fn); maxlevel = SC_MAX ((int) r->level, maxlevel); ++quadrants_per_level[r->level]; @@ -1489,8 +1492,7 @@ p4est_complete_region (p4est_t * p4est, /* R <- R + b */ if (include_q2) { - r = p4est_quadrant_array_push (quadrants); - *r = b; + r = p4est_quadrant_array_push_copy (quadrants, &b); p4est_quadrant_init_data (p4est, which_tree, r, init_fn); maxlevel = SC_MAX ((int) r->level, maxlevel); ++quadrants_per_level[r->level]; @@ -1577,8 +1579,8 @@ p4est_complete_or_balance_kernel (sc_array_t * inlist, sc_array_t * out, p4est_quadrant_t * first_desc, p4est_quadrant_t * last_desc, - size_t * count_in, size_t * count_out, - size_t * count_an) + size_t *count_in, size_t *count_out, + size_t *count_an) { int inserted; size_t iz, jz; @@ -1659,8 +1661,7 @@ p4est_complete_or_balance_kernel (sc_array_t * inlist, } else { /* add tempq to inlist */ - q = (p4est_quadrant_t *) sc_array_push (inlist); - *q = tempq; + q = p4est_quadrant_array_push_copy (inlist, &tempq); q->p.user_int = 0; incount++; } @@ -1910,7 +1911,6 @@ p4est_complete_or_balance_kernel (sc_array_t * inlist, /* merge valid quadrants from outlist into inlist */ ocount = outlist[l].elem_count; - q = NULL; for (jz = 0; jz < ocount; ++jz) { /* go through output list */ qpointer = (p4est_quadrant_t **) sc_array_index (&outlist[l], jz); @@ -1929,8 +1929,7 @@ p4est_complete_or_balance_kernel (sc_array_t * inlist, continue; } if (qalloc->p.user_int != precluded) { - q = p4est_quadrant_array_push (inlist); - *q = *qalloc; + (void) p4est_quadrant_array_push_copy (inlist, qalloc); } sc_mempool_free (qpool, qalloc); } @@ -1980,7 +1979,7 @@ p4est_complete_or_balance_kernel (sc_array_t * inlist, p4est_quadrant_successor (last_desc, &ld); P4EST_ASSERT (p4est_quadrant_is_ancestor (dom, &ld)); q = &ld; -#ifdef P4EST_DEBUG +#ifdef P4EST_ENABLE_DEBUG P4EST_QUADRANT_INIT (&ld_old); p4est_quadrant_linear_id_ext128 (last_desc, P4EST_QMAXLEVEL, &lid); p4est_lid_add_inplace (&lid, &one); @@ -2004,8 +2003,7 @@ p4est_complete_or_balance_kernel (sc_array_t * inlist, break; } /* add tempq to out */ - r = (p4est_quadrant_t *) sc_array_push (out); - *r = tempq; + (void) p4est_quadrant_array_push_copy (out, &tempq); /* if tempq is a last sibling, go up a level */ while (tempq.level >= minlevel && pid == P4EST_CHILDREN - 1) { @@ -2061,7 +2059,7 @@ p4est_complete_or_balance_kernel (sc_array_t * inlist, p4est_quadrant_successor (last_desc, &ld); P4EST_ASSERT (p4est_quadrant_is_ancestor (dom, &ld)); q = &ld; -#ifdef P4EST_DEBUG +#ifdef P4EST_ENABLE_DEBUG P4EST_QUADRANT_INIT (&ld_old); p4est_quadrant_linear_id_ext128 (last_desc, P4EST_QMAXLEVEL, &lid); p4est_lid_add_inplace (&lid, &one); @@ -2253,7 +2251,7 @@ p4est_complete_or_balance (p4est_t * p4est, p4est_topidx_t which_tree, outlist = sc_array_new (sizeof (p4est_quadrant_t)); /* get the reduced representation of the tree */ - q = (p4est_quadrant_t *) sc_array_push (inlist); + q = p4est_quadrant_array_push (inlist); p = p4est_quadrant_array_index (tquadrants, 0); p4est_quadrant_sibling (p, q, 0); for (iz = 1; iz < tcount; iz++) { @@ -2266,7 +2264,7 @@ p4est_complete_or_balance (p4est_t * p4est, p4est_topidx_t which_tree, } continue; } - q = (p4est_quadrant_t *) sc_array_push (inlist); + q = p4est_quadrant_array_push (inlist); p4est_quadrant_sibling (p, q, 0); } @@ -2563,8 +2561,7 @@ p4est_balance_border (p4est_t * p4est, p4est_connect_type_t btype, } continue; } - q = (p4est_quadrant_t *) sc_array_push (inlist); - *q = *r; + q = p4est_quadrant_array_push_copy (inlist, r); } fcount = flist->elem_count; @@ -2871,7 +2868,9 @@ p4est_partition_given (p4est_t * p4est, MPI_Request *recv_request, *send_request; #endif #ifdef P4EST_ENABLE_DEBUG + int send_to_empty; unsigned crc; + p4est_gloidx_t my_begin_comp, my_end_comp; p4est_gloidx_t total_requested_quadrants = 0; #endif @@ -3101,10 +3100,16 @@ p4est_partition_given (p4est_t * p4est, to_begin = rank; to_end = rank; memset (begin_send_to, -1, num_procs * sizeof (p4est_gloidx_t)); +#ifdef P4EST_ENABLE_DEBUG + send_to_empty = 1; +#endif } else { p4est_find_partition (num_procs, new_global_last_quad_index, my_begin, my_end, &to_begin, &to_end); +#ifdef P4EST_ENABLE_DEBUG + send_to_empty = 0; +#endif for (to_proc = to_begin; to_proc <= to_end; ++to_proc) { /* I send to to_proc which may be empty */ lower_bound = @@ -3189,27 +3194,29 @@ p4est_partition_given (p4est_t * p4est, /* Set the num_per_tree_local */ num_per_tree_local = P4EST_ALLOC_ZERO (p4est_locidx_t, num_send_trees); - to_proc = rank; - my_base = (rank == 0) ? 0 : (global_last_quad_index[rank - 1] + 1); - my_begin = begin_send_to[to_proc] - my_base; - my_end = begin_send_to[to_proc] + num_send_to[to_proc] - 1 - my_base; - for (which_tree = first_local_tree; which_tree <= last_local_tree; - ++which_tree) { - tree = p4est_tree_array_index (trees, which_tree); + if (num_send_to[rank] > 0) { + to_proc = rank; + my_base = (rank == 0) ? 0 : (global_last_quad_index[rank - 1] + 1); + my_begin = begin_send_to[to_proc] - my_base; + my_end = begin_send_to[to_proc] + num_send_to[to_proc] - 1 - my_base; + for (which_tree = first_local_tree; which_tree <= last_local_tree; + ++which_tree) { + tree = p4est_tree_array_index (trees, which_tree); - from_begin = (which_tree == first_local_tree) ? 0 : - (local_tree_last_quad_index[which_tree - 1] + 1); - from_end = local_tree_last_quad_index[which_tree]; + from_begin = (which_tree == first_local_tree) ? 0 : + (local_tree_last_quad_index[which_tree - 1] + 1); + from_end = local_tree_last_quad_index[which_tree]; - if (from_begin <= my_end && from_end >= my_begin) { - /* Need to copy from tree which_tree */ - tree_from_begin = SC_MAX (my_begin, from_begin) - from_begin; - tree_from_end = SC_MIN (my_end, from_end) - from_begin; - num_copy_global = tree_from_end - tree_from_begin + 1; - P4EST_ASSERT (num_copy_global >= 0); - P4EST_ASSERT (num_copy_global <= (p4est_gloidx_t) P4EST_LOCIDX_MAX); - num_copy = (p4est_locidx_t) num_copy_global; - num_per_tree_local[which_tree - first_local_tree] = num_copy; + if (from_begin <= my_end && from_end >= my_begin) { + /* Need to copy from tree which_tree */ + tree_from_begin = SC_MAX (my_begin, from_begin) - from_begin; + tree_from_end = SC_MIN (my_end, from_end) - from_begin; + num_copy_global = tree_from_end - tree_from_begin + 1; + P4EST_ASSERT (num_copy_global >= 0); + P4EST_ASSERT (num_copy_global <= (p4est_gloidx_t) P4EST_LOCIDX_MAX); + num_copy = (p4est_locidx_t) num_copy_global; + num_per_tree_local[which_tree - first_local_tree] = num_copy; + } } } @@ -3303,7 +3310,7 @@ p4est_partition_given (p4est_t * p4est, /* Fill in forest */ mpiret = - MPI_Waitall (num_proc_recv_from, recv_request, MPI_STATUSES_IGNORE); + sc_MPI_Waitall (num_proc_recv_from, recv_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); #endif @@ -3381,8 +3388,18 @@ p4est_partition_given (p4est_t * p4est, last_tree = /* same type */ SC_MAX (last_local_tree, new_last_local_tree); my_base = (rank == 0) ? 0 : (global_last_quad_index[rank - 1] + 1); - my_begin = begin_send_to[rank] - my_base; - my_end = begin_send_to[rank] + num_send_to[rank] - 1 - my_base; + /* Although begin_send_to[rank] may be -1 if my_begin > my_end was true above + * it is valid to use just 0 in the conditional expressions below since + * my_begin > my_end is also true for the new values and since it holds + * 0 <= from_begin <= from_end and therefore the related if-statement is + * false as in the old version of the code (cf. debug mode) and the exact + * values of the new my_begin and my_end values are not needed for empty + * processors. + */ + my_begin = ((to_begin_global_quad <= rank && to_end_global_quad >= rank) ? + begin_send_to[rank] : 0) - my_base; + my_end = ((to_begin_global_quad <= rank && to_end_global_quad >= rank) ? + begin_send_to[rank] : 0) + num_send_to[rank] - 1 - my_base; for (which_tree = first_tree; which_tree <= last_tree; ++which_tree) { tree = p4est_tree_array_index (trees, which_tree); @@ -3397,6 +3414,27 @@ p4est_partition_given (p4est_t * p4est, (local_tree_last_quad_index[which_tree - 1] + 1); from_end = local_tree_last_quad_index[which_tree]; +#ifdef P4EST_ENABLE_DEBUG + if (send_to_empty) { + /* The corner case that begin_send_to[rank] == -1 holds. Therefore, + * we need to ensure that from_begin <= my_end && from_end >= my_begin + * is still evaluated to false even if begin_send_to[rank] == 0 was + * assumed in the calculation of my_{begin,end}. + * The reasoning of this is already presented above but + * here we check this resoning again with an assertion. + */ + P4EST_ASSERT (!(from_begin <= my_end && from_end >= my_begin)); + /* We also check if the evaluation of the expression mentioned + * above coincides with the evaluation without the adjustment + * in the calculation of my_{begin,end}. Both assertions combined + * give us that the behaviour of the code is not affected. + */ + my_begin_comp = -1 - my_base; + my_end_comp = -1 + num_send_to[rank] - 1 - my_base; + P4EST_ASSERT (!(from_begin <= my_end_comp && + from_end >= my_begin_comp)); + } +#endif if (from_begin <= my_end && from_end >= my_begin) { /* Need to keep part of tree which_tree */ tree_from_begin = SC_MAX (my_begin, from_begin) - from_begin; @@ -3614,7 +3652,8 @@ p4est_partition_given (p4est_t * p4est, /* Clean up */ #ifdef P4EST_ENABLE_MPI - mpiret = MPI_Waitall (num_proc_send_to, send_request, MPI_STATUSES_IGNORE); + mpiret = + sc_MPI_Waitall (num_proc_send_to, send_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); #ifdef P4EST_ENABLE_DEBUG @@ -3661,3 +3700,38 @@ p4est_partition_given (p4est_t * p4est, return total_quadrants_shipped; } + +int +p4est_quadrant_on_face_boundary (p4est_t * p4est, p4est_topidx_t treeid, + int face, const p4est_quadrant_t * q) +{ + p4est_qcoord_t dh, xyz; + p4est_connectivity_t *conn = p4est->connectivity; + + P4EST_ASSERT (0 <= face && face < P4EST_FACES); + P4EST_ASSERT (p4est_quadrant_is_valid (q)); + + if (conn->tree_to_tree[P4EST_FACES * treeid + face] != treeid || + (int) conn->tree_to_face[P4EST_FACES * treeid + face] != face) { + return 0; + } + + dh = P4EST_LAST_OFFSET (q->level); + switch (face / 2) { + case 0: + xyz = q->x; + break; + case 1: + xyz = q->y; + break; +#ifdef P4_TO_P8 + case 2: + xyz = q->z; + break; +#endif + default: + SC_ABORT_NOT_REACHED (); + break; + } + return xyz == ((face & 0x01) ? dh : 0); +} diff --git a/src/p4est_algorithms.h b/src/p4est_algorithms.h index e29153df5..27c390dad 100644 --- a/src/p4est_algorithms.h +++ b/src/p4est_algorithms.h @@ -33,11 +33,15 @@ #ifndef P4EST_ALGORITHMS_H #define P4EST_ALGORITHMS_H -#include #include SC_EXTERN_C_BEGIN; +/** Create a memory pool for quadrants that initializes compiler padding. + * \return Initialized mempool with zero_and_persist setting. + */ +sc_mempool_t *p4est_quadrant_mempool_new (void); + /** Alloc and initialize the user data of a valid quadrant. * \param [in] which_tree 0-based index of this quadrant's tree. * \param [in,out] quad The quadrant to be initialized. @@ -333,6 +337,22 @@ p4est_gloidx_t p4est_partition_given (p4est_t * p4est, const p4est_locidx_t * num_quadrants_in_proc); +/** Checks if a quadrant's face is on the boundary of the forest. + * + * \param [in] p4est The forest in which to search for \a q + * \param [in] treeid The tree to which \a q belongs. + * \param [in] q The quadrant that is in question. + * \param [in] face The face of the quadrant that is in question. + * + * \return true if the quadrant's face is on the boundary of the forest and + * false otherwise. + */ +int p4est_quadrant_on_face_boundary (p4est_t * p4est, + p4est_topidx_t treeid, + int face, + const p4est_quadrant_t * + q); + SC_EXTERN_C_END; #endif /* !P4EST_ALGORITHMS_H */ diff --git a/src/p4est_base.c b/src/p4est_base.c index 63576e701..15f244c33 100644 --- a/src/p4est_base.c +++ b/src/p4est_base.c @@ -25,6 +25,7 @@ #include int p4est_package_id = -1; +int p4est_initialized = 0; void p4est_init (sc_log_handler_t log_handler, int log_threshold) @@ -45,6 +46,30 @@ p4est_init (sc_log_handler_t log_handler, int log_threshold) P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "CFLAGS", P4EST_CFLAGS); P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "LDFLAGS", P4EST_LDFLAGS); P4EST_GLOBAL_PRODUCTIONF ("%-*s %s\n", w, "LIBS", P4EST_LIBS); + + p4est_initialized = 1; +} + +int +p4est_is_initialized (void) +{ + return p4est_initialized; +} + +int +p4est_have_zlib (void) +{ +#ifndef P4EST_HAVE_ZLIB + return 0; +#else + return sc_have_zlib (); +#endif +} + +int +p4est_get_package_id (void) +{ + return p4est_package_id; } #ifndef __cplusplus @@ -68,8 +93,6 @@ p4est_init (sc_log_handler_t log_handler, int log_threshold) #undef P4EST_LERRORF #endif -#ifndef SC_SPLINT - void P4EST_GLOBAL_LOGF (int priority, const char *fmt, ...) { @@ -140,5 +163,3 @@ p4est_version_minor (void) /* In rare cases SC_VERSION_MAJOR may be a non-numerical string */ return sc_atoi (SC_TOSTRING (P4EST_VERSION_MINOR)); } - -#endif diff --git a/src/p4est_base.h b/src/p4est_base.h index a9dd930a9..25c9c9028 100644 --- a/src/p4est_base.h +++ b/src/p4est_base.h @@ -322,13 +322,13 @@ void P4EST_LERRORF (const char *fmt, ...) extern int p4est_package_id; static inline void -p4est_log_indent_push () +p4est_log_indent_push (void) { sc_log_indent_push_count (p4est_package_id, 1); } static inline void -p4est_log_indent_pop () +p4est_log_indent_pop (void) { sc_log_indent_pop_count (p4est_package_id, 1); } @@ -345,6 +345,30 @@ p4est_log_indent_pop () void p4est_init (sc_log_handler_t log_handler, int log_threshold); +/** Return whether p4est has been initialized or not. + * Keep in mind that \ref p4est_init is an optional function + * but it helps with proper parallel logging. + * + * Currently there is no inverse to \ref p4est_init, and no way to deinit it. + * This is ok since initialization generally does no harm. + * Just do not call libsc's finalize function while p4est is still in use. + * + * \return True if p4est has been initialized with a call to + * \ref p4est_init and false otherwise. + */ +int p4est_is_initialized (void); + +/** Check for a sufficiently recent zlib installation. + * \return True if zlib is detected in both sc and p4est. + */ +int p4est_have_zlib (void); + +/** Query the package identity as registered in libsc. + * \return This is -1 before \ref p4est_init has been called + * and a proper package identifier (>= 0) afterwards. + */ +int p4est_get_package_id (void); + /** Compute hash value for two p4est_topidx_t integers. * \param [in] tt Array of (at least) two values. * \return An unsigned hash value. diff --git a/src/p4est_bits.c b/src/p4est_bits.c index 9c744dd02..2785ebd62 100644 --- a/src/p4est_bits.c +++ b/src/p4est_bits.c @@ -359,41 +359,34 @@ p4est_quadrant_is_equal_piggy (const p4est_quadrant_t * q1, } int -p4est_quadrant_compare (const void *v1, const void *v2) +p4est_coordinates_compare (const p4est_qcoord_t v1[], + const p4est_qcoord_t v2[]) { - const p4est_quadrant_t *q1 = (const p4est_quadrant_t *) v1; - const p4est_quadrant_t *q2 = (const p4est_quadrant_t *) v2; - uint32_t exclorx, exclory, exclorxy, exclor; #ifdef P4_TO_P8 uint32_t exclorz; #endif int64_t p1, p2, diff; - P4EST_ASSERT (p4est_quadrant_is_node (q1, 1) || - p4est_quadrant_is_extended (q1)); - P4EST_ASSERT (p4est_quadrant_is_node (q2, 1) || - p4est_quadrant_is_extended (q2)); - /* these are unsigned variables that inherit the sign bits */ - exclorx = q1->x ^ q2->x; - exclory = q1->y ^ q2->y; + exclorx = v1[0] ^ v2[0]; + exclory = v1[1] ^ v2[1]; exclor = exclorxy = exclorx | exclory; #ifdef P4_TO_P8 - exclorz = q1->z ^ q2->z; + exclorz = v1[2] ^ v2[2]; exclor = exclorxy | exclorz; #endif if (!exclor) { - return (int) q1->level - (int) q2->level; + return 0; } #ifdef P4_TO_P8 /* if (exclor ^ exclorz) > exclorz, then exclorxy has a more significant bit * than exclorz; also exclor and (exclor ^ exclorz) cannot be equal */ if (exclorz > (exclor ^ exclorz)) { - p1 = q1->z + ((q1->z >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); - p2 = q2->z + ((q2->z >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); + p1 = v1[2] + ((v1[2] >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); + p2 = v2[2] + ((v2[2] >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); } else #if 0 @@ -401,17 +394,45 @@ p4est_quadrant_compare (const void *v1, const void *v2) #endif #endif if (exclory > (exclorxy ^ exclory)) { - p1 = q1->y + ((q1->y >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); - p2 = q2->y + ((q2->y >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); + p1 = v1[1] + ((v1[1] >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); + p2 = v2[1] + ((v2[1] >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); } else { - p1 = q1->x + ((q1->x >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); - p2 = q2->x + ((q2->x >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); + p1 = v1[0] + ((v1[0] >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); + p2 = v2[0] + ((v2[0] >= 0) ? 0 : ((int64_t) 1 << (P4EST_MAXLEVEL + 2))); } diff = p1 - p2; return (diff == 0) ? 0 : ((diff < 0) ? -1 : 1); } +int +p4est_quadrant_compare (const void *v1, const void *v2) +{ + const p4est_quadrant_t *q1 = (const p4est_quadrant_t *) v1; + const p4est_quadrant_t *q2 = (const p4est_quadrant_t *) v2; + + p4est_qcoord_t a[P4EST_DIM], b[P4EST_DIM]; + int coord_diff; + + P4EST_ASSERT (p4est_quadrant_is_node (q1, 1) || + p4est_quadrant_is_extended (q1)); + P4EST_ASSERT (p4est_quadrant_is_node (q2, 1) || + p4est_quadrant_is_extended (q2)); + + a[0] = q1->x; + a[1] = q1->y; +#ifdef P4_TO_P8 + a[2] = q1->z; +#endif + b[0] = q2->x; + b[1] = q2->y; +#ifdef P4_TO_P8 + b[2] = q2->z; +#endif + coord_diff = p4est_coordinates_compare (a, b); + return coord_diff ? coord_diff : ((int) q1->level - (int) q2->level); +} + int p4est_quadrant_disjoint (const void *a, const void *b) { @@ -635,16 +656,31 @@ p4est_quadrant_child_id (const p4est_quadrant_t * q) return p4est_quadrant_ancestor_id (q, (int) q->level); } +int +p4est_coordinates_is_inside_root (const p4est_qcoord_t coord[]) +{ + /* *INDENT-OFF* */ + return (coord[0] >= 0 && coord[0] < P4EST_ROOT_LEN) && + (coord[1] >= 0 && coord[1] < P4EST_ROOT_LEN) && +#ifdef P4_TO_P8 + (coord[2] >= 0 && coord[2] < P4EST_ROOT_LEN) && +#endif + /* *INDENT-ON* */ + 1; +} + int p4est_quadrant_is_inside_root (const p4est_quadrant_t * q) { - return - (q->x >= 0 && q->x < P4EST_ROOT_LEN) && - (q->y >= 0 && q->y < P4EST_ROOT_LEN) && + p4est_qcoord_t coord[P4EST_DIM]; + + coord[0] = q->x; + coord[1] = q->y; #ifdef P4_TO_P8 - (q->z >= 0 && q->z < P4EST_ROOT_LEN) && + coord[2] = q->z; #endif - 1; + + return p4est_coordinates_is_inside_root (coord); } int @@ -714,16 +750,30 @@ p4est_quadrant_is_node (const p4est_quadrant_t * q, int inside) } int -p4est_quadrant_is_valid (const p4est_quadrant_t * q) +p4est_coordinates_is_valid (const p4est_qcoord_t coord[], int level) { return - (q->level >= 0 && q->level <= P4EST_QMAXLEVEL) && - ((q->x & (P4EST_QUADRANT_LEN (q->level) - 1)) == 0) && - ((q->y & (P4EST_QUADRANT_LEN (q->level) - 1)) == 0) && + (level >= 0 && level <= P4EST_QMAXLEVEL) && + ((coord[0] & (P4EST_QUADRANT_LEN (level) - 1)) == 0) && + ((coord[1] & (P4EST_QUADRANT_LEN (level) - 1)) == 0) && #ifdef P4_TO_P8 - ((q->z & (P4EST_QUADRANT_LEN (q->level) - 1)) == 0) && + ((coord[2] & (P4EST_QUADRANT_LEN (level) - 1)) == 0) && +#endif + p4est_coordinates_is_inside_root (coord); +} + +int +p4est_quadrant_is_valid (const p4est_quadrant_t * q) +{ + p4est_qcoord_t coord[P4EST_DIM]; + + coord[0] = q->x; + coord[1] = q->y; +#ifdef P4_TO_P8 + coord[2] = q->z; #endif - p4est_quadrant_is_inside_root (q); + + return p4est_coordinates_is_valid (coord, q->level); } int @@ -1451,8 +1501,7 @@ p4est_quadrant_corner_neighbor_extra (const p4est_quadrant_t * q, p4est_quadrant_corner_neighbor (q, corner, &temp); if (p4est_quadrant_is_inside_root (&temp)) { - qp = p4est_quadrant_array_push (quads); - *qp = temp; + qp = p4est_quadrant_array_push_copy (quads, &temp); tp = (p4est_topidx_t *) sc_array_push (treeids); *tp = t; if (ncorners != NULL) { @@ -1464,7 +1513,7 @@ p4est_quadrant_corner_neighbor_extra (const p4est_quadrant_t * q, if (!p4est_quadrant_is_outside_corner (&temp)) { #ifndef P4_TO_P8 - qp = (p4est_quadrant_t *) sc_array_push (quads); + qp = p4est_quadrant_array_push (quads); tp = (p4est_topidx_t *) sc_array_push (treeids); face = p4est_corner_faces[corner][0]; @@ -1834,6 +1883,73 @@ p4est_nearest_common_ancestor_D (const p4est_quadrant_t * q1, P4EST_ASSERT (p4est_quadrant_is_extended (r)); } +void +p4est_coordinates_transform_face (const p4est_qcoord_t coords_in[], + p4est_qcoord_t coords_out[], + const int ftransform[]) +{ + p4est_qcoord_t *target_xyz[P4EST_DIM]; + const p4est_qcoord_t *my_xyz[P4EST_DIM]; + const int *my_axis = &ftransform[0]; + const int *target_axis = &ftransform[3]; + const int *edge_reverse = &ftransform[6]; + +#ifdef P4EST_ENABLE_DEBUG + int i; + + for (i = 0; i < 3; ++i) { + P4EST_ASSERT (0 <= my_axis[i] && my_axis[i] < P4EST_DIM); + P4EST_ASSERT (0 <= target_axis[i] && target_axis[i] < P4EST_DIM); + } +#endif + + P4EST_ASSERT (my_axis[0] != my_axis[2]); + P4EST_ASSERT (target_axis[0] != target_axis[2]); + P4EST_ASSERT (0 <= edge_reverse[0] && edge_reverse[0] < 2); + P4EST_ASSERT (0 <= edge_reverse[2] && edge_reverse[2] < 4); +#ifdef P4_TO_P8 + P4EST_ASSERT (my_axis[0] != my_axis[1] && my_axis[1] != my_axis[2]); + P4EST_ASSERT (target_axis[0] != target_axis[1] && + target_axis[1] != target_axis[2]); + P4EST_ASSERT (0 <= edge_reverse[1] && edge_reverse[1] < 2); +#else + P4EST_ASSERT (my_axis[1] == 0 && target_axis[1] == 0); + P4EST_ASSERT (edge_reverse[1] == 0); +#endif + P4EST_ASSERT (coords_in != coords_out); + + for (int d = 0; d < P4EST_DIM; d++) { + my_xyz[d] = &coords_in[d]; + target_xyz[d] = &coords_out[d]; + } + + *target_xyz[target_axis[0]] = + !edge_reverse[0] ? *my_xyz[my_axis[0]] : P4EST_ROOT_LEN - + *my_xyz[my_axis[0]]; +#ifdef P4_TO_P8 + *target_xyz[target_axis[1]] = + !edge_reverse[1] ? *my_xyz[my_axis[1]] : P4EST_ROOT_LEN - + *my_xyz[my_axis[1]]; +#endif + switch (edge_reverse[2]) { + case 0: + *target_xyz[target_axis[2]] = -*my_xyz[my_axis[2]]; + break; + case 1: + *target_xyz[target_axis[2]] = *my_xyz[my_axis[2]] + P4EST_ROOT_LEN; + break; + case 2: + *target_xyz[target_axis[2]] = *my_xyz[my_axis[2]] - P4EST_ROOT_LEN; + break; + case 3: + *target_xyz[target_axis[2]] = + P4EST_ROOT_LEN + (P4EST_ROOT_LEN - *my_xyz[my_axis[2]]); + break; + default: + SC_ABORT_NOT_REACHED (); + } +} + void p4est_quadrant_transform_face (const p4est_quadrant_t * q, p4est_quadrant_t * r, const int ftransform[]) @@ -2405,3 +2521,170 @@ p4est_quadrant_srand (const p4est_quadrant_t * q, sc_rand_state_t * rstate) #endif } } + +void +p4est_neighbor_transform_quadrant (const p4est_neighbor_transform_t * nt, + const p4est_quadrant_t * self_quad, + p4est_quadrant_t * neigh_quad) +{ + p4est_qcoord_t self_from_origin[2][P4EST_DIM]; + p4est_qcoord_t neigh_from_origin[2][P4EST_DIM]; + p4est_qcoord_t h = P4EST_QUADRANT_LEN (self_quad->level); + + self_from_origin[0][0] = self_quad->x - nt->origin_self[0]; + self_from_origin[0][1] = self_quad->y - nt->origin_self[1]; +#ifdef P4_TO_P8 + self_from_origin[0][2] = self_quad->z - nt->origin_self[2]; +#endif + + for (int d = 0; d < P4EST_DIM; d++) { + self_from_origin[1][d] = self_from_origin[0][d] + h; + } + + for (int i = 0; i < 2; i++) { + for (int d = 0; d < P4EST_DIM; d++) { + neigh_from_origin[i][d] = + nt->sign[d] * self_from_origin[i][nt->perm[d]]; + } + } + + neigh_quad->x = + SC_MIN (neigh_from_origin[0][0], + neigh_from_origin[1][0]) + nt->origin_neighbor[0]; + neigh_quad->y = + SC_MIN (neigh_from_origin[0][1], + neigh_from_origin[1][1]) + nt->origin_neighbor[1]; +#ifdef P4_TO_P8 + neigh_quad->z = + SC_MIN (neigh_from_origin[0][2], + neigh_from_origin[1][2]) + nt->origin_neighbor[2]; +#endif + neigh_quad->level = self_quad->level; +} + +void +p4est_neighbor_transform_quadrant_reverse (const p4est_neighbor_transform_t * + nt, + const p4est_quadrant_t * + neigh_quad, + p4est_quadrant_t * self_quad) +{ + p4est_qcoord_t neigh_from_origin[2][P4EST_DIM]; + p4est_qcoord_t self_from_origin[2][P4EST_DIM]; + p4est_qcoord_t h = P4EST_QUADRANT_LEN (neigh_quad->level); + + neigh_from_origin[0][0] = neigh_quad->x - nt->origin_neighbor[0]; + neigh_from_origin[0][1] = neigh_quad->y - nt->origin_neighbor[1]; +#ifdef P4_TO_P8 + neigh_from_origin[0][2] = neigh_quad->z - nt->origin_neighbor[2]; +#endif + + for (int d = 0; d < P4EST_DIM; d++) { + neigh_from_origin[1][d] = neigh_from_origin[0][d] + h; + } + + for (int i = 0; i < 2; i++) { + for (int d = 0; d < P4EST_DIM; d++) { + self_from_origin[i][nt->perm[d]] = + nt->sign[d] * neigh_from_origin[i][d]; + } + } + + self_quad->x = + SC_MIN (self_from_origin[0][0], + self_from_origin[1][0]) + nt->origin_self[0]; + self_quad->y = + SC_MIN (self_from_origin[0][1], + self_from_origin[1][1]) + nt->origin_self[1]; +#ifdef P4_TO_P8 + self_quad->z = + SC_MIN (self_from_origin[0][2], + self_from_origin[1][2]) + nt->origin_self[2]; +#endif + self_quad->level = neigh_quad->level; +} + +int +p4est_quadrant_is_ancestor_face (const p4est_quadrant_t * descendant, + const p4est_quadrant_t * ancestor, int face) +{ + p4est_qcoord_t dx, ax; + + P4EST_ASSERT (p4est_quadrant_is_valid (descendant)); + P4EST_ASSERT (p4est_quadrant_is_valid (ancestor)); + P4EST_ASSERT (p4est_quadrant_is_ancestor (ancestor, descendant)); + P4EST_ASSERT (0 <= face && face < P4EST_FACES); + + switch (face >> 1) { + case 0: + dx = descendant->x; + ax = ancestor->x; + break; + case 1: + dx = descendant->y; + ax = ancestor->y; + break; +#ifdef P4_TO_P8 + case 2: + dx = descendant->z; + ax = ancestor->z; + break; +#endif + default: + SC_ABORT_NOT_REACHED (); + } + if (face & 0x01) { + dx += P4EST_QUADRANT_LEN (descendant->level); + ax += P4EST_QUADRANT_LEN (ancestor->level); + } + + return dx == ax; +} + +int +p4est_quadrant_is_ancestor_corner (const p4est_quadrant_t * descendant, + const p4est_quadrant_t * ancestor, + int corner) +{ + p4est_qcoord_t ax, ay, dx, dy, al, dl; +#ifdef P4_TO_P8 + p4est_qcoord_t az, dz; +#endif + + P4EST_ASSERT (p4est_quadrant_is_valid (ancestor)); + P4EST_ASSERT (p4est_quadrant_is_valid (descendant)); + P4EST_ASSERT (p4est_quadrant_is_ancestor (ancestor, descendant)); + P4EST_ASSERT (0 <= corner && corner < P4EST_CHILDREN); + + ax = ancestor->x; + ay = ancestor->y; + al = P4EST_QUADRANT_LEN (ancestor->level); + dx = descendant->x; + dy = descendant->y; + dl = P4EST_QUADRANT_LEN (descendant->level); +#ifdef P4_TO_P8 + az = ancestor->z; + dz = descendant->z; +#endif + + if (corner & 0x01) { + ax += al; + dx += dl; + } + if (corner & 0x02) { + ay += al; + dy += dl; + } +#ifdef P4_TO_P8 + if (corner & 0x04) { + az += al; + dz += dl; + } +#endif + + return (ax == dx && ay == dy +#ifdef P4_TO_P8 + && az == dz +#endif + ); +} diff --git a/src/p4est_bits.h b/src/p4est_bits.h index ce809c24e..f8a944cc0 100644 --- a/src/p4est_bits.h +++ b/src/p4est_bits.h @@ -88,6 +88,18 @@ int p4est_quadrant_is_equal_piggy (const p4est_quadrant_t * */ int p4est_quadrant_compare (const void *v1, const void *v2); +/** Compare two sets of coordinates in their Morton ordering. + * Coordinates are signed, but the sorted order will treat them + * as unsigned, with negative coordinates being greater than + * positive coordinates because of their representation in twos-complement. + * \param [in] v1, v2 Two sets of 2d coordinates. + * \return Returns < 0 if \a v1 < \a v2, + * 0 if \a v1 == \a v2, + * > 0 if \a v1 > \a v2 + */ +int p4est_coordinates_compare (const p4est_qcoord_t v1[], + const p4est_qcoord_t v2[]); + /** Compare two quadrants in their Morton ordering, with equivalence if the * two quadrants overlap. * \return Returns < 0 if \a v1 < \a v2 and \a v1 and \a v2 do not overlap, @@ -176,6 +188,13 @@ int p4est_quadrant_ancestor_id (const p4est_quadrant_t * q, */ int p4est_quadrant_child_id (const p4est_quadrant_t * q); +/** Test if Morton indices are inside the unit tree. + * \param [in] coord 2d coordinates. + * \return Returns true if \a (coord[0],coord[1]) is inside the unit tree. + */ +int p4est_coordinates_is_inside_root (const p4est_qcoord_t + coord[]); + /** Test if a quadrant is inside the unit tree. * \param [in] q Quadrant to be tested. * \return Returns true if \a q is inside the unit tree. @@ -212,6 +231,14 @@ int p4est_quadrant_is_outside_corner (const p4est_quadrant_t * int p4est_quadrant_is_node (const p4est_quadrant_t * q, int inside); +/** Test if Morton indices are valid and are inside the unit tree. + * \param [in] coord 2d coordinates. + * \param [in] level level + * \return Returns true if \a (coord[0],coord[1],level) is valid. + */ +int p4est_coordinates_is_valid (const p4est_qcoord_t coord[], + int level); + /** Test if a quadrant has valid Morton indices and is inside the unit tree. * \param [in] q Quadrant to be tested. * \return Returns true if \a q is valid. @@ -403,18 +430,18 @@ void p4est_quadrant_face_neighbor (const p4est_quadrant_t * q, /** Compute the face neighbor of a quadrant, transforming across tree * boundaries if necessary. * \param [in] q Input quadrant, must be valid. - * \param [in] t Tree that contains \q. + * \param [in] t Tree that contains \a q. * \param [in] face The face across which to generate the neighbor. * \param [in,out] r Existing quadrant whose Morton index will be filled. - * By convention, if there is no tree across \face, - * \r has the same Morton index as \q. - * \param [in,out] nface if not NULL, set to the face of \r that neighbors - * \q. nface is encoded with orientation information + * By convention, if there is no tree across \a face, + * \a r has the same Morton index as \a q. + * \param [in,out] nface if not NULL, set to the face of \a r that neighbors + * \a q. nface is encoded with orientation information * in the same manner as the tree_to_face array in * the p4est_connectivity_t struct. * \param [in] conn The connectivity structure for the forest. - * \return Returns the tree that contains \r. By convention, if there is no - * tree across \face, then -1 is returned. + * \return Returns the tree that contains \a r. By convention, if there is no + * tree across \a face, then -1 is returned. */ p4est_topidx_t p4est_quadrant_face_neighbor_extra (const p4est_quadrant_t * q, p4est_topidx_t t, @@ -478,14 +505,14 @@ void p4est_quadrant_corner_neighbor (const p4est_quadrant_t * * boundaries if necessary. Only computes neighbors that are not face or edge * neighbors. * \param [in] q Input quadrant, must be valid. - * \param [in] t Tree that contains \q. + * \param [in] t Tree that contains \a q. * \param [in] corner The corner across which to generate the neighbor. * \param [in,out] quads An initialized but empty array where the corner * neighbors will be placed. * \param [in,out] treeids An initialized but empty array where the ids of the * trees containing the corner neighbors will be placed. * \param [in,out] ncorners if not NULL, filled with the corners of \a quads - * that neighbor \q. + * that neighbor \a q. * \param [in] conn The connectivity structure for the forest. */ void p4est_quadrant_corner_neighbor_extra (const @@ -612,12 +639,27 @@ void p4est_nearest_common_ancestor_D (const p4est_quadrant_t * * [3,5] The coordinate axis sequence of the target face. * [6,8] Edge reverse flag for axis 0; face code for 1. * [1,4,7] 0 (unused for compatibility with 3D). - * \note \a q and \q r may NOT point to the same quadrant structure. + * \note \a q and \a r may NOT point to the same quadrant structure. */ void p4est_quadrant_transform_face (const p4est_quadrant_t * q, p4est_quadrant_t * r, const int ftransform[]); +/** Transforms coordinates across a face between trees. + * \param [in] coords_in Input coordinates. + * \param [out] coords_out Output coordinates. + * \param [in] ftransform This array holds 9 integers. + * [0,2] The coordinate axis sequence of the origin face. + * [3,5] The coordinate axis sequence of the target face. + * [6,8] Edge reverse flag for axis 0; face code for 1. + * [1,4,7] 0 (unused for compatibility with 3D). + */ +void p4est_coordinates_transform_face (const p4est_qcoord_t + coords_in[], + p4est_qcoord_t + coords_out[], + const int ftransform[]); + /** Checks if a quadrant touches a corner (diagonally inside or outside). */ int p4est_quadrant_touches_corner (const p4est_quadrant_t * q, @@ -696,6 +738,61 @@ void p4est_quadrant_predecessor (const p4est_quadrant_t * void p4est_quadrant_srand (const p4est_quadrant_t * q, sc_rand_state_t * rstate); +/** Transform a quadrant from self's coordinate system to neighbor's coordinate system. + * + * \param [in] nt A neighbor transform. + * \param [in] self_quad Input quadrant in self coordinates. + * \param [out] neigh_coords Quad transformed into neighbor coordinates. + * + * \note This transform gives meaningful results when \a self_quad is inside + * the tree root or touches the interface between the two trees in the + * transform. + */ +void p4est_neighbor_transform_quadrant + (const p4est_neighbor_transform_t * nt, + const p4est_quadrant_t * self_quad, p4est_quadrant_t * neigh_quad); + +/** Transform a quadrant from a neighbors's coordinate system to self's coordinate system. + * + * \param [in] nt A neighbor transform. + * \param [in] neigh_coords Input quadrant in neighbor coordinates. + * \param [out] self_coords Quad transformed into self coordinates. + * + * \note This transform gives meaningful results when \a neigh_quad is inside + * the tree root or touches the interface between the two trees in the + * transform. + */ +void p4est_neighbor_transform_quadrant_reverse + (const p4est_neighbor_transform_t * nt, + const p4est_quadrant_t * neigh_quad, p4est_quadrant_t * self_quad); + +/** Check if a descendant shares a face with a (strict) ancestor. + * + * \param [in] descendant The descendant in question. + * \param [in] ancestor The ancestor must not be equal to the descendant. + * \param [in] face The face of the descendant. + * + * \return true if descendant face touches ancestor face, false otherwise. +*/ +int p4est_quadrant_is_ancestor_face (const p4est_quadrant_t * + descendant, + const p4est_quadrant_t * + ancestor, int face); + +/** Check if a descendant shares a corner with a (strict) ancestor. + * + * \param [in] descendant The descendant in question. + * \param [in] ancestor The ancestor must not be equal to the descendant. + * \param [in] corner The corner of the descendant. + * + * \return true if descendant face touches ancestor corner, false otherwise. +*/ +int p4est_quadrant_is_ancestor_corner (const p4est_quadrant_t + * descendant, + const p4est_quadrant_t + * ancestor, + int corner); + SC_EXTERN_C_END; #endif /* !P4EST_BITS_H */ diff --git a/src/p4est_build.c b/src/p4est_build.c index 88b8fbcda..481cc93c7 100644 --- a/src/p4est_build.c +++ b/src/p4est_build.c @@ -141,7 +141,7 @@ p4est_build_new (p4est_t * from, size_t data_size, if (p4est->data_size > 0) { p4est->user_data_pool = sc_mempool_new (p4est->data_size); } - p4est->quadrant_pool = sc_mempool_new (sizeof (p4est_quadrant_t)); + p4est->quadrant_pool = p4est_quadrant_mempool_new (); /* initialize context structure */ build->init_fn = init_fn; diff --git a/src/p4est_communication.c b/src/p4est_communication.c index e01f80c0e..b4aa05d52 100644 --- a/src/p4est_communication.c +++ b/src/p4est_communication.c @@ -36,6 +36,23 @@ #include #endif +int +p4est_bsearch_partition (p4est_gloidx_t target, + const p4est_gloidx_t * gfq, int nmemb) +{ + size_t res; + + P4EST_ASSERT (nmemb > 0); + P4EST_ASSERT (gfq[0] <= target); + P4EST_ASSERT (target < gfq[nmemb]); + + res = sc_bsearch_range (&target, gfq, (size_t) nmemb, + sizeof (p4est_gloidx_t), p4est_gloidx_compare); + P4EST_ASSERT (res < (size_t) nmemb); + + return (int) res; +} + void p4est_comm_parallel_env_assign (p4est_t * p4est, sc_MPI_Comm mpicomm) { @@ -391,6 +408,23 @@ p4est_comm_global_partition (p4est_t * p4est, p4est_quadrant_t * first_quad) } } +void +p4est_comm_global_first_quadrant (p4est_gloidx_t global_num_quadrants, + int mpisize, p4est_gloidx_t * gfq) +{ + int i; + + P4EST_ASSERT (gfq != NULL); + P4EST_ASSERT (mpisize >= 1); + P4EST_ASSERT (global_num_quadrants >= 0); + + gfq[0] = 0; + for (i = 1; i < mpisize; ++i) { + gfq[i] = p4est_partition_cut_gloidx (global_num_quadrants, i, mpisize); + } + gfq[mpisize] = global_num_quadrants; +} + void p4est_comm_count_pertree (p4est_t * p4est, p4est_gloidx_t * pertree) { @@ -562,19 +596,31 @@ p4est_comm_count_pertree (p4est_t * p4est, p4est_gloidx_t * pertree) } int -p4est_comm_is_empty (p4est_t * p4est, int p) +p4est_comm_is_empty (p4est_t *p4est, int p) { - const p4est_gloidx_t *gfq; - P4EST_ASSERT (p4est != NULL); - P4EST_ASSERT (0 <= p && p < p4est->mpisize); + return p4est_comm_is_empty_gfq (p4est->global_first_quadrant, + p4est->mpisize, p); +} - gfq = p4est->global_first_quadrant; +int +p4est_comm_is_empty_gfq (const p4est_gloidx_t *gfq, int num_procs, int p) +{ P4EST_ASSERT (gfq != NULL); + P4EST_ASSERT (0 <= p && p < num_procs); return gfq[p] == gfq[p + 1]; } +int +p4est_comm_is_empty_gfp (const p4est_quadrant_t *gfp, int num_procs, int p) +{ + P4EST_ASSERT (gfp != NULL); + P4EST_ASSERT (0 <= p && p < num_procs); + + return p4est_quadrant_is_equal_piggy (&gfp[p], &gfp[p + 1]); +} + int p4est_comm_is_contained (p4est_t * p4est, p4est_locidx_t which_tree, const p4est_quadrant_t * q, int rank) @@ -623,22 +669,33 @@ p4est_comm_is_contained (p4est_t * p4est, p4est_locidx_t which_tree, } int -p4est_comm_is_owner (p4est_t * p4est, p4est_locidx_t which_tree, - const p4est_quadrant_t * q, int rank) +p4est_comm_is_owner (p4est_t *p4est, p4est_locidx_t which_tree, + const p4est_quadrant_t *q, int rank) +{ + P4EST_ASSERT (p4est != NULL); + P4EST_ASSERT (p4est->connectivity != NULL); + + return p4est_comm_is_owner_gfp + (p4est->global_first_position, p4est->mpisize, + p4est->connectivity->num_trees, which_tree, q, rank); +} + +int p4est_comm_is_owner_gfp + (const p4est_quadrant_t *gfp, int num_procs, + p4est_topidx_t num_trees, p4est_locidx_t which_tree, + const p4est_quadrant_t *q, int rank) { p4est_topidx_t ctree; const p4est_quadrant_t *cur; - P4EST_ASSERT (p4est != NULL && p4est->connectivity != NULL); - P4EST_ASSERT (p4est->global_first_position != NULL); - P4EST_ASSERT (0 <= which_tree && - which_tree < p4est->connectivity->num_trees); + P4EST_ASSERT (gfp != NULL); + P4EST_ASSERT (0 <= which_tree && which_tree < num_trees); P4EST_ASSERT (q != NULL); - P4EST_ASSERT (0 <= rank && rank < p4est->mpisize); + P4EST_ASSERT (0 <= rank && rank < num_procs); P4EST_ASSERT (p4est_quadrant_is_node (q, 1) || p4est_quadrant_is_valid (q)); /* check whether q begins on a lower processor than rank */ - cur = &p4est->global_first_position[rank]; + cur = &gfp[rank]; P4EST_ASSERT (cur->level == P4EST_QMAXLEVEL); ctree = cur->p.which_tree; if (which_tree < ctree || @@ -654,7 +711,7 @@ p4est_comm_is_owner (p4est_t * p4est, p4est_locidx_t which_tree, /* check whether q lies fully on a higher processor than rank */ ++cur; - P4EST_ASSERT (cur == &p4est->global_first_position[rank + 1]); + P4EST_ASSERT (cur == &gfp[rank + 1]); P4EST_ASSERT (cur->level == P4EST_QMAXLEVEL); ctree = cur->p.which_tree; if (which_tree > ctree || @@ -885,29 +942,22 @@ p4est_comm_checksum (p4est_t * p4est, unsigned local_crc, size_t local_bytes) #ifdef P4EST_ENABLE_MPI int mpiret; int p; - uint64_t send[2]; - uint64_t *gather; - - send[0] = (uint64_t) local_crc; - send[1] = (uint64_t) local_bytes; - gather = NULL; - if (p4est->mpirank == 0) { - gather = P4EST_ALLOC (uint64_t, 2 * p4est->mpisize); - } - mpiret = sc_MPI_Gather (send, 2, sc_MPI_LONG_LONG_INT, - gather, 2, sc_MPI_LONG_LONG_INT, 0, p4est->mpicomm); + long long send[2]; + long long *gather; + + send[0] = (long long) local_crc; + send[1] = (long long) local_bytes; + gather = P4EST_ALLOC (long long, 2 * p4est->mpisize); + mpiret = sc_MPI_Allgather (send, 2, sc_MPI_LONG_LONG_INT, + gather, 2, sc_MPI_LONG_LONG_INT, p4est->mpicomm); SC_CHECK_MPI (mpiret); - if (p4est->mpirank == 0) { - for (p = 1; p < p4est->mpisize; ++p) { - crc = adler32_combine (crc, (uLong) gather[2 * p + 0], - (z_off_t) gather[2 * p + 1]); - } - P4EST_FREE (gather); - } - else { - crc = 0; + crc = (uLong) gather[0]; + for (p = 1; p < p4est->mpisize; ++p) { + crc = adler32_combine (crc, (uLong) gather[2 * p + 0], + (z_off_t) gather[2 * p + 1]); } + P4EST_FREE (gather); #endif /* P4EST_ENABLE_MPI */ return (unsigned) crc; @@ -958,23 +1008,6 @@ p4est_transfer_assign_comm (const p4est_gloidx_t * dest_gfq, src_gfq[*mpirank + 1] <= src_gfq[*mpisize]); } -int -p4est_bsearch_partition (p4est_gloidx_t target, - const p4est_gloidx_t * gfq, int nmemb) -{ - size_t res; - - P4EST_ASSERT (nmemb > 0); - P4EST_ASSERT (gfq[0] <= target); - P4EST_ASSERT (target < gfq[nmemb]); - - res = sc_bsearch_range (&target, gfq, (size_t) nmemb, - sizeof (p4est_gloidx_t), p4est_gloidx_compare); - P4EST_ASSERT (res < (size_t) nmemb); - - return (int) res; -} - p4est_transfer_context_t * p4est_transfer_fixed_begin (const p4est_gloidx_t * dest_gfq, const p4est_gloidx_t * src_gfq, diff --git a/src/p4est_communication.h b/src/p4est_communication.h index ea48c2ecc..52909e0a5 100644 --- a/src/p4est_communication.h +++ b/src/p4est_communication.h @@ -29,6 +29,23 @@ SC_EXTERN_C_BEGIN; +/** Given target, find index p such that `gfq[p] <= target < gfq[p + 1]`. + * \param[in] target The value that is searched in \a gfq. \a target + * has to satisfy `gfq[0] <= target < gfq[nmemb]`. + * \param[in] gfq The sorted array (ascending) in that the function will + * search. + * \param [in] nmemb Number of entries in array MINUS ONE. + * \return Index p such that `gfq[p] <= target < gfq[p + 1]`. + * \note This function differs from \ref p4est_find_partiton + * since \ref p4est_find_partition searches for two + * targets using binary search in an optimized way + * but \ref p4est_bsearch_partition only performs a + * single binary search. + */ +int p4est_bsearch_partition (p4est_gloidx_t target, + const p4est_gloidx_t * gfq, + int nmemb); + /** Assign an MPI communicator to p4est; retrieve parallel environment. * * \param [in] mpicomm A valid MPI communicator. @@ -102,7 +119,7 @@ int p4est_comm_parallel_env_reduce_ext (p4est_t ** int add_to_beginning, int **ranks_subcomm); -/** Caculate the number and partition of quadrants. +/** Calculate the number and partition of quadrants. * \param [in,out] p4est Adds all \c p4est->local_num_quadrant counters and * puts cumulative sums in p4est->global_first_quadrant. */ @@ -120,6 +137,21 @@ void p4est_comm_global_partition (p4est_t * p4est, p4est_quadrant_t * first_quad); +/** Calculate the global fist quadrant array for a uniform partition. + * + * \param [in] global_num_quadrants The global number of quadrants. + * \param [in] mpisize The number of MPI ranks. + * \param [in,out] gfq At least allocated mpisize + 1 + * p4est_gloidx_t. This array is + * filled with the global first + * quadrant array assuming a + * uniform partition. + */ +void p4est_comm_global_first_quadrant (p4est_gloidx_t + global_num_quadrants, + int mpisize, + p4est_gloidx_t * gfq); + /** Compute and distribute the cumulative number of quadrants per tree. * \param [in] p4est This p4est needs to have correct values for * global_first_quadrant and global_first_position. @@ -134,7 +166,27 @@ void p4est_comm_count_pertree (p4est_t * p4est, * \param [in] p Valid processor id. * \return True if and only if processor \p is empty. */ -int p4est_comm_is_empty (p4est_t * p4est, int p); +int p4est_comm_is_empty (p4est_t *p4est, int p); + +/** Query whether a processor has no quadrants. + * \param [in] gfq An array encoding the partition offsets in the + * global quadrant array; length \a num_procs + 1. + * \param [in] num_procs Number of processes in the partition. + * \param [in] p Valid 0 <= \a p < \a num_procs. + * \return True if and only if processor \a p is empty. + */ +int p4est_comm_is_empty_gfq (const p4est_gloidx_t *gfq, + int num_procs, int p); + +/** Query whether a processor has no quadrants. + * \param [in] gfp An array encoding the partition shape. + * Non-decreasing; length \a num_procs + 1. + * \param [in] num_procs Number of processes in the partition. + * \param [in] p Valid 0 <= \a p < \a num_procs. + * \return True if and only if processor \a p is empty. + */ +int p4est_comm_is_empty_gfp (const p4est_quadrant_t *gfp, + int num_procs, int p); /** Test whether a quadrant is fully contained in a rank's owned region. * This function may return false when \ref p4est_comm_is_owner returns true. @@ -147,19 +199,38 @@ int p4est_comm_is_contained (p4est_t * p4est, const p4est_quadrant_t * q, int rank); -/** Test ownershop of a quadrant via p4est->global_first_position. +/** Test ownership of a quadrant via p4est->global_first_position. * The quadrant is considered owned if its first descendant is owned. - * This, a positive result occurs even if its last descendant overlaps + * Thus, a positive result occurs even if its last descendant overlaps * a higher process. - * \param [in] rank Rank whose ownership is tested. - * Assumes a forest with no overlaps. - * \return true if rank is the owner of the first descendant. + * \param [in] p4est Valid forest. + * \param [in] which_tree Valid tree number wrt. the forest. + * \param [in] q Valid quadrant wrt. the forest. + * \param [in] rank Rank whose ownership is tested. + * \return True if rank is the owner of the first descendant. */ -int p4est_comm_is_owner (p4est_t * p4est, +int p4est_comm_is_owner (p4est_t *p4est, p4est_locidx_t which_tree, - const p4est_quadrant_t * q, + const p4est_quadrant_t *q, int rank); +/** Test ownership of a quadrant via a global_first_position array. + * This array encodes part of the partition of a valid forest object. + * The quadrant is considered owned if its first descendant is owned. + * Thus, a positive result occurs even if its last descendant overlaps + * a higher process. + * \param [in] gfp Position array of length \a num_procs + 1. + * \param [in] num_procs Number of processes in this context. + * \param [in] num_trees Number of trees in this context. + * \param [in] which_tree Valid tree number wrt. the forest. + * \param [in] q Valid quadrant wrt. the forest. + * \param [in] rank Rank whose ownership is tested. + * \return True if rank is the owner of the first descendant. + */ +int p4est_comm_is_owner_gfp + (const p4est_quadrant_t *gfp, int num_procs, p4est_topidx_t num_trees, + p4est_locidx_t which_tree, const p4est_quadrant_t *q, int rank); + /** Searches the owner of a quadrant via p4est->global_first_position. * Assumes a tree with no overlaps. * \param [in] guess Initial guess for the search. @@ -213,27 +284,16 @@ int p4est_comm_sync_flag (p4est_t * p4est, /** Compute a parallel partition-independent checksum out of local checksums. * This checksum depends on the global refinement topology. * It does not depend on how the mesh is partitioned. - * The result is available on rank 0. + * The result is available on all processors. * \param [in] p4est The MPI information of this p4est will be used. * \param [in] local_crc Locally computed adler32 checksum. * \param [in] local_bytes Number of bytes used for local checksum. - * \return Parallel checksum on rank 0, 0 otherwise. + * \return Parallel checksum on all processors. */ unsigned p4est_comm_checksum (p4est_t * p4est, unsigned local_crc, size_t local_bytes); -/** Compute a parallel partition-dependent checksum out of local checksums. - * This checksum depends on both the global refinement topology and partition. - * \param [in] p4est The MPI information of this p4est will be used. - * \param [in] local_crc Locally computed adler32 checksum. - * \param [in] local_bytes Number of bytes used for local checksum. - * \return Parallel checksum on rank 0, 0 otherwise. - */ -unsigned p4est_comm_checksum_partition (p4est_t * p4est, - unsigned local_crc, - size_t local_bytes); - /** Context data to allow for split begin/end data transfer. */ typedef struct p4est_transfer_context { @@ -279,23 +339,6 @@ void p4est_transfer_fixed (const p4est_gloidx_t * dest_gfq, const void *src_data, size_t data_size); -/** Given target, find index p such that `gfq[p] <= target < gfq[p + 1]`. - * \param[in] target The value that is searched in \a gfq. \a target - * has to satisfy `gfq[0] <= target < gfq[nmemb]`. - * \param[in] gfq The sorted array (ascending) in that the function will - * search. - * \param [in] nmemb Number of entries in array MINUS ONE. - * \return Index p such that `gfq[p] <= target < gfq[p + 1]`. - * \note This function differs from \ref p4est_find_partiton - * since \ref p4est_find_partition searches for two - * targets using binary search in an optimized way - * but \ref p4est_bsearch_partition only performs a - * single binary search. - */ -int p4est_bsearch_partition (p4est_gloidx_t target, - const p4est_gloidx_t * gfq, - int nmemb); - /** Initiate a fixed-size data transfer between partitions. * See \ref p4est_transfer_fixed for a full description. * This functions calls asynchronous MPI send/receive and returns. diff --git a/src/p4est_connectivity.c b/src/p4est_connectivity.c index 9d892d305..4a1498b5c 100644 --- a/src/p4est_connectivity.c +++ b/src/p4est_connectivity.c @@ -24,6 +24,7 @@ #ifndef P4_TO_P8 #include +#include #endif #ifdef P4EST_WITH_METIS #include @@ -1218,7 +1219,6 @@ p4est_connectivity_inflate (sc_array_t * buffer) SC_CHECK_ABORT (source != NULL, "source open from buffer"); conn = p4est_connectivity_source (source); - SC_CHECK_ABORT (conn != NULL, "source connectivity"); retval = sc_io_source_destroy (source); SC_CHECK_ABORT (retval == 0, "destroy source"); @@ -1421,6 +1421,125 @@ p4est_connectivity_new_rotwrap (void) corner_to_tree, corner_to_corner); } +p4est_connectivity_t * +p4est_connectivity_new_circle (void) +{ + const p4est_topidx_t num_vertices = 12; + const p4est_topidx_t num_trees = 6; + const p4est_topidx_t num_ctt = 0; + const double vertices[12 * 3] = { + /* inner hexagon */ + 0.0, 1.0, 0.0, + 0.866025404, 0.5, 0.0, + 0.866025404, -0.5, 0.0, + 0, -1.0, 0.0, + -0.866025404, -0.5, 0.0, + -0.866025404, 0.5, 0.0, + /* outer hexagon */ + 0.0, 2.0, 0.0, + 1.73205081, 1.0, 0.0, + 1.73205081, -1.0, 0.0, + 0, -2.0, 0.0, + -1.73205081, -1.0, 0.0, + -1.73205081, 1.0, 0.0, + }; + const p4est_topidx_t tree_to_vertex[6 * 4] = { + 7, 6, 1, 0, + 11, 5, 6, 0, + 5, 11, 4, 10, + 9, 3, 10, 4, + 2, 3, 8, 9, + 8, 7, 2, 1, + }; + const p4est_topidx_t tree_to_tree[6 * 4] = { + 5, 1, 0, 0, + 1, 1, 2, 0, + 2, 2, 1, 3, + 3, 3, 4, 2, + 5, 3, 4, 4, + 4, 0, 5, 5, + }; + const int8_t tree_to_face[6 * 4] = { + 1, 3, 2, 3, + 0, 1, 6, 1, + 0, 1, 6, 7, + 0, 1, 5, 7, + 4, 6, 2, 3, + 4, 0, 2, 3, + }; + + return p4est_connectivity_new_copy (num_vertices, num_trees, 0, + vertices, tree_to_vertex, + tree_to_tree, tree_to_face, + NULL, &num_ctt, NULL, NULL); +} + +p4est_connectivity_t * +p4est_connectivity_new_drop (void) +{ +/* *INDENT-OFF* */ + const p4est_topidx_t num_vertices = 10; + const p4est_topidx_t num_trees = 5; + const p4est_topidx_t num_ctt = 1; + const double vertices[10 * 3] = { + 0, 0, 0, + 1, 0, 0, + 3, 0, 0, + 0, 1, 0, + 1, 1, 0, + 2, 1, 0, + 1, 2, 0, + 2, 2, 0, + 0, 3, 0, + 3, 3, 0, + }; + const p4est_topidx_t tree_to_vertex[5 * 4] = { + 0, 1, 3, 4, + 1, 2, 4, 5, + 5, 2, 7, 9, + 6, 7, 8, 9, + 3, 4, 8, 6, + }; + const p4est_topidx_t tree_to_tree[5 * 4] = { + 0, 1, 0, 4, + 0, 2, 1, 1, + 2, 2, 1, 3, + 4, 2, 3, 3, + 4, 4, 0, 3, + }; + const int8_t tree_to_face[5 * 4] = { + 0, 0, 2, 2, + 1, 2, 2, 3, + 0, 1, 1, 1, + 3, 3, 2, 3, + 0, 1, 3, 0, + }; + + const p4est_topidx_t tree_to_corner[5 * 4] = { + -1, -1, -1, 0, + -1, -1, 0, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, 0, -1, -1, + }; + const p4est_topidx_t ctt_offset[1 + 1] = { + 0, 3 + }; + const p4est_topidx_t corner_to_tree[3] = { + 0, 1, 4, + }; + const int8_t corner_to_corner[3] = { + 3, 2, 1, + }; +/* *INDENT-ON* */ + + return p4est_connectivity_new_copy (num_vertices, num_trees, num_ctt, + vertices, tree_to_vertex, + tree_to_tree, tree_to_face, + tree_to_corner, ctt_offset, + corner_to_tree, corner_to_corner); +} + p4est_connectivity_t * p4est_connectivity_new_corner (void) { @@ -1658,7 +1777,6 @@ p4est_connectivity_new_cubed (void) p4est_connectivity_t * p4est_connectivity_new_icosahedron (void) { - /* *INDENT-OFF* */ const p4est_topidx_t num_vertices = 22; const p4est_topidx_t num_trees = 10; @@ -1757,23 +1875,13 @@ p4est_connectivity_new_icosahedron (void) 2, 2, 2, 2, 2,/* corner 0 (i.e vertex 0) */ 1, 1, 1, 1, 1,/* corner 1 (i.e vertex 17) */ }; - - /* *INDENT-ON* */ - p4est_connectivity_t *conn = - p4est_connectivity_new_copy (num_vertices, num_trees, num_corners, - vertices, tree_to_vertex, - tree_to_tree, tree_to_face, - tree_to_corner, ctt_offset, - corner_to_tree, corner_to_corner); - - P4EST_GLOBAL_INFOF ("Is connectivity ok : %d\n", - p4est_connectivity_is_valid (conn)); - P4EST_ASSERT (p4est_connectivity_is_valid (conn)); - - return conn; - + return p4est_connectivity_new_copy (num_vertices, num_trees, num_corners, + vertices, tree_to_vertex, + tree_to_tree, tree_to_face, + tree_to_corner, ctt_offset, + corner_to_tree, corner_to_corner); } p4est_connectivity_t * @@ -1823,18 +1931,11 @@ p4est_connectivity_new_shell2d (void) }; /* *INDENT-ON* */ - p4est_connectivity_t *conn = - p4est_connectivity_new_copy (num_vertices, num_trees, 0, - vertices, tree_to_vertex, - tree_to_tree, tree_to_face, - NULL, &num_ctt, NULL, NULL); - - /* printf("\nconnectivity good : %d\n",p4est_connectivity_is_valid (conn)); */ - P4EST_ASSERT (p4est_connectivity_is_valid (conn)); - - return conn; - -} /* p4est_connectivity_new_shell2d */ + return p4est_connectivity_new_copy (num_vertices, num_trees, 0, + vertices, tree_to_vertex, + tree_to_tree, tree_to_face, + NULL, &num_ctt, NULL, NULL); +} p4est_connectivity_t * p4est_connectivity_new_disk2d (void) @@ -1876,19 +1977,61 @@ p4est_connectivity_new_disk2d (void) /* *INDENT-ON* */ - p4est_connectivity_t *conn = - p4est_connectivity_new_copy (num_vertices, num_trees, num_corners, - vertices, tree_to_vertex, - tree_to_tree, tree_to_face, - NULL, &num_ctt, NULL, NULL); - - P4EST_GLOBAL_INFOF ("Is connectivity ok : %d\n", - p4est_connectivity_is_valid (conn)); - P4EST_ASSERT (p4est_connectivity_is_valid (conn)); + return p4est_connectivity_new_copy (num_vertices, num_trees, num_corners, + vertices, tree_to_vertex, + tree_to_tree, tree_to_face, + NULL, &num_ctt, NULL, NULL); +} - return conn; +p4est_connectivity_t * +p4est_connectivity_new_bowtie (void) +{ +/* *INDENT-OFF* */ + const p4est_topidx_t num_vertices = 7; + const p4est_topidx_t num_trees = 2; + const p4est_topidx_t num_corners = 1; + const double vertices[7 * 3] = { + -0.7071, 0.7071, 0, + 0.7071, 0.7071, 0, + -1.4142, 0, 0, + 0, 0, 0, + 1.4142, 0, 0, + -0.7071, -0.7071, 0, + 0.7071, -0.7071, 0, + }; + const p4est_topidx_t tree_to_vertex[2 * 4] = { + 2, 5, 0, 3, /* tree 0 */ + 6, 4, 3, 1, /* tree 1 */ + }; + const p4est_topidx_t tree_to_tree[2 * 4] = { + 0, 0, 0, 0, /* tree 0 */ + 1, 1, 1, 1, /* tree 1 */ + }; + const int8_t tree_to_face[5 * 4] = { + 0, 1, 2, 3, /* tree 0 */ + 0, 1, 2, 3, /* tree 1 */ + }; + const p4est_topidx_t tree_to_corner[2 * 4] = { + -1, -1, -1, 0,/* tree 0 */ + -1, -1, 0, -1,/* tree 1 */ + }; + const p4est_topidx_t corner_to_tree[2] = { + 0, 1, + }; + const p4est_topidx_t ctt_offset[1 + 1] = { + 0, 2, + }; + const int8_t corner_to_corner[2] = { + 3, 2, + }; +/* *INDENT-ON* */ -} /* p4est_connectivity_new_disk2d */ + return p4est_connectivity_new_copy (num_vertices, num_trees, num_corners, + vertices, tree_to_vertex, + tree_to_tree, tree_to_face, + tree_to_corner, ctt_offset, + corner_to_tree, corner_to_corner); +} p4est_connectivity_t * p4est_connectivity_new_disk_nonperiodic (void) @@ -2765,6 +2908,9 @@ p4est_connectivity_new_byname (const char *name) else if (!strcmp (name, "disk2d")) { return p4est_connectivity_new_disk2d (); } + else if (!strcmp (name, "bowtie")) { + return p4est_connectivity_new_bowtie (); + } else if (!strcmp (name, "unit")) { return p4est_connectivity_new_unitsquare (); } @@ -4194,7 +4340,7 @@ p4est_connectivity_join_corners (p4est_connectivity_t * conn, n1 = endt - startt; /* the number of tree corners that border c1 */ for (it = startt; it < endt; it++) { /* get all trees that reference c1 */ p4est_topidx_t nt = conn->corner_to_tree[it]; /* nt is a tree the borders c1 */ - int ntc = (int) conn->corner_to_corner[it]; /* ntc is nt's numering for c1 */ + int ntc = (int) conn->corner_to_corner[it]; /* ntc is nt's numbering for c1 */ conn->tree_to_corner[P4EST_CHILDREN * nt + ntc] = c0; /* c1->c0 */ } @@ -4299,11 +4445,11 @@ p8est_connectivity_join_edges (p8est_connectivity_t * conn, n1 = endt - startt; /* the number of tree edges that border e1 */ for (it = startt; it < endt; it++) { /* get all trees that reference e1 */ p4est_topidx_t nt = conn->edge_to_tree[it]; /* nt is a tree the borders e1 */ - int nte = (int) conn->edge_to_edge[it]; /* nte is nt's numering for e1, + int nte = (int) conn->edge_to_edge[it]; /* nte is nt's numbering for e1, modified by orientation */ int o = nte / P8EST_EDGES; /* o is that modifying orientation */ - nte %= P8EST_EDGES; /* okay, now nte is nt's numering for e1 */ + nte %= P8EST_EDGES; /* okay, now nte is nt's numbering for e1 */ conn->tree_to_edge[P8EST_EDGES * nt + nte] = e0; /* e1->e0 */ /* if edge_left and edge_right have opposite orientations, then the * orientation information in edge_to_edge has to be toggled */ @@ -4860,3 +5006,311 @@ p4est_connectivity_read_inp (const char *filename) } return NULL; } + +/* *INDENT-OFF* */ +static p4est_neighbor_transform_t *p4est_neighbor_transform_array_push + (sc_array_t *array) +{ + return (p4est_neighbor_transform_t *) sc_array_push (array); +} +/* *INDENT-ON* */ + +static void +p4est_face_transform_to_neighbor_transform (const int ftransform[9], + p4est_neighbor_transform_t * nt) +{ + const int *my_axis = &ftransform[0]; + const int *target_axis = &ftransform[3]; + const int *edge_reverse = &ftransform[6]; +#ifndef P4_TO_P8 + int ids[] = { 0, 2 }; +#else + int ids[] = { 0, 1, 2 }; +#endif + int8_t sign2; + p4est_qcoord_t o_self2, o_neigh2; + + for (int di = 0; di < P4EST_DIM; di++) { + int d = ids[di]; + + nt->perm[target_axis[d]] = my_axis[d]; + } + for (int d = 0; d < P4EST_DIM - 1; d++) { + nt->sign[target_axis[d]] = edge_reverse[d] ? -1 : 1; + nt->origin_neighbor[target_axis[d]] = P4EST_ROOT_LEN / 2; + nt->origin_self[my_axis[d]] = P4EST_ROOT_LEN / 2; + } + switch (edge_reverse[2]) { + case 0: + sign2 = -1; + o_self2 = 0; + o_neigh2 = 0; + break; + case 1: + sign2 = 1; + o_self2 = 0; + o_neigh2 = P4EST_ROOT_LEN; + break; + case 2: + sign2 = 1; + o_self2 = P4EST_ROOT_LEN; + o_neigh2 = 0; + break; + case 3: + sign2 = -1; + o_self2 = P4EST_ROOT_LEN; + o_neigh2 = P4EST_ROOT_LEN; + break; + default: + SC_ABORT_NOT_REACHED (); + } + nt->sign[target_axis[2]] = sign2; + nt->origin_self[my_axis[2]] = o_self2; + nt->origin_neighbor[target_axis[2]] = o_neigh2; +} + +#ifdef P4_TO_P8 +static void +p8est_edge_transform_to_neighbor_transform (const p8est_edge_transform_t * et, + int8_t iedge, + p4est_neighbor_transform_t * nt) +{ + const int other_axes[3][2] = { {1, 2}, {0, 2}, {0, 1} }; + int iaxis = iedge / 4; + int naxis = et->naxis[0]; + + nt->perm[naxis] = iaxis; + nt->perm[other_axes[naxis][0]] = other_axes[iaxis][0]; + nt->perm[other_axes[naxis][1]] = other_axes[iaxis][1]; + + nt->origin_self[iaxis] = P4EST_ROOT_LEN / 2; + nt->origin_self[other_axes[iaxis][0]] = (iedge & 1) ? P4EST_ROOT_LEN : 0; + nt->origin_self[other_axes[iaxis][1]] = (iedge & 2) ? P4EST_ROOT_LEN : 0; + + nt->origin_neighbor[naxis] = P4EST_ROOT_LEN / 2; + nt->origin_neighbor[other_axes[naxis][0]] = + (et->corners & 1) ? P4EST_ROOT_LEN : 0; + nt->origin_neighbor[other_axes[naxis][1]] = + (et->corners & 2) ? P4EST_ROOT_LEN : 0; + + nt->sign[naxis] = et->nflip ? -1 : 1; + nt->sign[other_axes[naxis][0]] = ((iedge ^ et->corners) & 1) ? 1 : -1; + nt->sign[other_axes[naxis][1]] = ((iedge ^ et->corners) & 2) ? 1 : -1; +} +#endif + +static void +p4est_corner_transform_to_neighbor_transform (p4est_corner_transform_t * ct, + int corner, + p4est_neighbor_transform_t * nt) +{ + for (int d = 0; d < P4EST_DIM; d++) { + nt->perm[d] = d; + nt->origin_self[d] = (corner & (1 << d)) ? P4EST_ROOT_LEN : 0; + nt->origin_neighbor[d] = (ct->ncorner & (1 << d)) ? P4EST_ROOT_LEN : 0; + nt->sign[d] = ((corner ^ ct->ncorner) & (1 << d)) ? 1 : -1; + } +} + +void +p4est_connectivity_get_neighbor_transforms (p4est_connectivity_t * conn, + p4est_topidx_t tree_id, + p4est_connect_type_t + boundary_type, + int boundary_index, + sc_array_t * + neighbor_transform_array) +{ +#ifdef P4EST_ENABLE_DEBUG + int index_lim; +#endif + int dim; + + P4EST_ASSERT (0 <= tree_id && tree_id < conn->num_trees); + P4EST_ASSERT (P4EST_CONNECT_SELF <= boundary_type + && boundary_type <= P4EST_CONNECT_FULL); + P4EST_ASSERT (neighbor_transform_array->elem_size == + sizeof (p4est_neighbor_transform_t)); + P4EST_ASSERT (boundary_index >= 0); + switch (boundary_type) { + case P4EST_CONNECT_SELF: +#ifdef P4EST_ENABLE_DEBUG + index_lim = 1; +#endif + dim = P4EST_DIM; + break; + case P4EST_CONNECT_FACE: +#ifdef P4EST_ENABLE_DEBUG + index_lim = P4EST_FACES; +#endif + dim = P4EST_DIM - 1; + break; + case P4EST_CONNECT_CORNER: +#ifdef P4EST_ENABLE_DEBUG + index_lim = P4EST_CHILDREN; +#endif + dim = 0; + break; +#ifdef P4_TO_P8 + case P8EST_CONNECT_EDGE: +#ifdef P4EST_ENABLE_DEBUG + index_lim = P8EST_EDGES; +#endif + dim = 1; + break; +#endif + default: + /* This can only happen for a invalid boundary type. */ + SC_ABORT_NOT_REACHED (); + } + P4EST_ASSERT (boundary_index < index_lim); + + /* always add self transformation */ + { + p4est_neighbor_transform_t *nt = p4est_neighbor_transform_array_push + (neighbor_transform_array); + + nt->neighbor_type = P4EST_CONNECT_SELF; + nt->neighbor = tree_id; + nt->index_self = nt->index_neighbor = 0; + for (int i = 0; i < P4EST_DIM; i++) { + nt->origin_self[i] = 0; + nt->origin_neighbor[i] = 0; + nt->perm[i] = i; + nt->sign[i] = 1; + } + } + if (boundary_type == P4EST_CONNECT_SELF) { + return; + } + + { + /* list of trees adjacent to the boundary point */ + int nfaces = (dim == P4EST_DIM - 1) ? 1 : +#ifdef P4_TO_P8 + (dim == 1) ? 2 : +#endif + P4EST_DIM; + const int *faces = (dim == P4EST_DIM - 1) ? &boundary_index : +#ifdef P4_TO_P8 + (dim == 1) ? &p8est_edge_faces[boundary_index][0] : +#endif + &p4est_corner_faces[boundary_index][0]; + const int8_t *to_face = &conn->tree_to_face[P4EST_FACES * tree_id]; + + for (int fi = 0; fi < nfaces; fi++) { + int f = faces[fi]; + int ftransform[9]; + int ntree = + p4est_find_face_transform (conn, tree_id, f, ftransform); + + if (ntree >= 0) { + p4est_neighbor_transform_t *nt = p4est_neighbor_transform_array_push + (neighbor_transform_array); + + nt->neighbor_type = P4EST_CONNECT_FACE; + nt->neighbor = ntree; + nt->index_self = f; + nt->index_neighbor = to_face[f] % P4EST_FACES; + p4est_face_transform_to_neighbor_transform (ftransform, nt); + } + } + } + if (boundary_type == P4EST_CONNECT_FACE) { + return; + } + +#ifdef P4_TO_P8 + { + int nedges = (dim == 1) ? 1 : 3; + const int *edges = + (dim == 1) ? &boundary_index : &p8est_corner_edges[boundary_index][0]; + + for (int ei = 0; ei < nedges; ei++) { + int e = edges[ei]; + p8est_edge_info_t e_info; + sc_array_t *eta = &e_info.edge_transforms; + + sc_array_init (eta, sizeof (p8est_edge_transform_t)); + p8est_find_edge_transform (conn, tree_id, e, &e_info); + for (size_t iz = 0; iz < eta->elem_count; iz++) { + p8est_edge_transform_t *et = + (p8est_edge_transform_t *) sc_array_index (eta, iz); + p4est_neighbor_transform_t *nt = + p4est_neighbor_transform_array_push (neighbor_transform_array); + + nt->neighbor_type = P8EST_CONNECT_EDGE; + nt->index_self = e; + nt->index_neighbor = et->nedge; + nt->neighbor = et->ntree; + p8est_edge_transform_to_neighbor_transform (et, e, nt); + } + sc_array_reset (eta); + } + + } + + if (boundary_type == P8EST_CONNECT_EDGE) { + return; + } +#endif + + { + p4est_corner_info_t c_info; + sc_array_t *cta = &c_info.corner_transforms; + + sc_array_init (cta, sizeof (p4est_corner_transform_t)); + p4est_find_corner_transform (conn, tree_id, boundary_index, &c_info); + for (size_t iz = 0; iz < cta->elem_count; iz++) { + p4est_neighbor_transform_t *nt = + p4est_neighbor_transform_array_push (neighbor_transform_array); + p4est_corner_transform_t *ct = + (p4est_corner_transform_t *) sc_array_index (cta, iz); + + nt->neighbor = ct->ntree; + nt->neighbor_type = P4EST_CONNECT_CORNER; + nt->index_self = boundary_index; + nt->index_neighbor = ct->ncorner; + p4est_corner_transform_to_neighbor_transform (ct, boundary_index, nt); + } + + sc_array_reset (cta); + } + +} + +void +p4est_neighbor_transform_coordinates (const p4est_neighbor_transform_t * nt, + const p4est_qcoord_t + self_coords[P4EST_DIM], + p4est_qcoord_t neigh_coords[P4EST_DIM]) +{ + p4est_qcoord_t self_from_origin[P4EST_DIM]; + + for (int d = 0; d < P4EST_DIM; d++) { + self_from_origin[d] = self_coords[d] - nt->origin_self[d]; + } + for (int d = 0; d < P4EST_DIM; d++) { + neigh_coords[d] = + nt->sign[d] * self_from_origin[nt->perm[d]] + nt->origin_neighbor[d]; + } +} + +void +p4est_neighbor_transform_coordinates_reverse (const p4est_neighbor_transform_t + * nt, + const p4est_qcoord_t + neigh_coords[P4EST_DIM], + p4est_qcoord_t + self_coords[P4EST_DIM]) +{ + p4est_qcoord_t neigh_from_origin[P4EST_DIM]; + + for (int d = 0; d < P4EST_DIM; d++) { + neigh_from_origin[d] = neigh_coords[d] - nt->origin_neighbor[d]; + } + for (int d = 0; d < P4EST_DIM; d++) { + self_coords[nt->perm[d]] = + nt->sign[d] * neigh_from_origin[d] + nt->origin_self[nt->perm[d]]; + } +} diff --git a/src/p4est_connectivity.h b/src/p4est_connectivity.h index e497b1c71..743c93c41 100644 --- a/src/p4est_connectivity.h +++ b/src/p4est_connectivity.h @@ -24,7 +24,33 @@ /** \file p4est_connectivity.h * - * The coarse topological description of the forest. + * The connectivity defines the coarse topology of the forest. + * + * A 2D forest consists of one or more quadtrees, each of which a logical + * square. + * Each tree has a local coordinate system, which defines the origin and the + * direction of its x- and y-axes as well as the numbering of its faces and + * corners. + * Each tree may connect to any other tree (including itself) across any of + * its faces and/or corners, where the neighbor may be arbitrarily rotated + * and/or flipped. + * The \ref p4est_connectivity data structure stores these connections. + * + * We impose the following requirement for consistency of \ref p4est_balance : + * + * \note If a connectivity implies natural connections between trees that + * are corner neighbors without being face neighbors, these corners shall be + * encoded explicitly in the connectivity. + * Please see the documentation of \ref p4est_connectivity_t for the exact + * encoding convention. + * + * We provide various predefined connectivitys by dedicated constructors, + * such as + * + * * \ref p4est_connectivity_new_unitsquare for the unit square, + * * \ref p4est_connectivity_new_periodic for the periodic unit square, + * * \ref p4est_connectivity_new_brick for a rectangular grid of trees, + * * \ref p4est_connectivity_new_moebius for a nonoriented loop of trees. * * \ingroup p4est */ @@ -45,9 +71,7 @@ SC_EXTERN_C_BEGIN; #define P4EST_DIM 2 /** The number of faces of a quadrant */ #define P4EST_FACES (2 * P4EST_DIM) -/** The number of children of a quadrant - * - * also the number of corners */ +/** The number of children of a quadrant, also the number of corners */ #define P4EST_CHILDREN 4 /** The number of children/corners touching one face */ #define P4EST_HALF (P4EST_CHILDREN / 2) @@ -63,13 +87,14 @@ SC_EXTERN_C_BEGIN; /** Exponentiate with dimension */ #define P4EST_DIM_POW(a) ((a) * (a)) -/* size of face transformation encoding */ +/** Data size of face transformation encoding */ #define P4EST_FTRANSFORM 9 /** p4est identification string */ #define P4EST_STRING "p4est" -/* Increase this number whenever the on-disk format for +/** The revision number of the p4est ondisk file format. + * Increase this number whenever the on-disk format for * p4est_connectivity, p4est, or any other 2D data structure changes. * The format for reading and writing must be the same. */ @@ -87,9 +112,11 @@ SC_EXTERN_C_BEGIN; typedef enum { /* make sure to have different values 2D and 3D */ - P4EST_CONNECT_FACE = 21, - P4EST_CONNECT_CORNER = 22, - P4EST_CONNECT_FULL = P4EST_CONNECT_CORNER + P4EST_CONNECT_SELF = 20, /**< No balance whatsoever. */ + P4EST_CONNECT_FACE = 21, /**< Balance across faces only. */ + P4EST_CONNECT_ALMOST = P4EST_CONNECT_FACE, /**< = CORNER - 1. */ + P4EST_CONNECT_CORNER = 22, /**< Balance across faces and corners. */ + P4EST_CONNECT_FULL = P4EST_CONNECT_CORNER /**< = CORNER. */ } p4est_connect_type_t; @@ -122,20 +149,25 @@ const char *p4est_connect_type_string (p4est_connect_type_t btype); * * The arrays tree_to_* are stored in z ordering. * For corners the order wrt. yx is 00 01 10 11. - * For faces the order is -x +x -y +y. - * They are allocated [0][0]..[0][3]..[num_trees-1][0]..[num_trees-1][3]. + * For faces the order is given by the normal directions -x +x -y +y. + * Each face has a natural direction by increasing face corner number. + * Face connections are allocated + * [0][0]..[0][3]..[num_trees-1][0]..[num_trees-1][3]. + * If a face is on the physical boundary it must connect to itself. * * The values for tree_to_face are 0..7 * where ttf % 4 gives the face number and ttf / 4 the face orientation code. - * The orientation is 0 for edges that are aligned in z-order, - * and 1 for edges that are running opposite in z-order. + * The orientation is 0 for faces that are mutually direction-aligned + * and 1 for faces that are running in opposite directions. * * It is valid to specify num_vertices as 0. * In this case vertices and tree_to_vertex are set to NULL. * Otherwise the vertex coordinates are stored in the array vertices as * [0][0]..[0][2]..[num_vertices-1][0]..[num_vertices-1][2]. + * Vertex coordinates are optional and not used for inferring topology. * - * The corners are only stored when they connect trees. + * The corners are stored when they connect trees that are not already face + * neighbors at that specific corner. * In this case tree_to_corner indexes into \a ctt_offset. * Otherwise the tree_to_corner entry must be -1 and this corner is ignored. * If num_corners == 0, tree_to_corner and corner_to_* arrays are set to NULL. @@ -147,6 +179,12 @@ const char *p4est_connect_type_string (p4est_connect_type_t btype); * The size of the corner_to_* arrays is num_ctt = ctt_offset[num_corners]. * * The *_to_attr arrays may have arbitrary contents defined by the user. + * We do not interpret them. + * + * \note + * If a connectivity implies natural connections between trees that are corner + * neighbors without being face neighbors, these corners shall be encoded + * explicitly in the connectivity. */ typedef struct p4est_connectivity { @@ -187,20 +225,87 @@ p4est_connectivity_t; size_t p4est_connectivity_memory_used (p4est_connectivity_t * conn); +/** Generic interface for transformations between a tree and any of its corner */ typedef struct { - p4est_topidx_t ntree; - int8_t ncorner; + p4est_topidx_t ntree; /**< The number of the tree*/ + int8_t ncorner; /**< The number of the corner*/ } p4est_corner_transform_t; +/** Information about the neighbors of a corner*/ typedef struct { - p4est_topidx_t icorner; - sc_array_t corner_transforms; + p4est_topidx_t icorner; /**< The number of the originating corner */ + sc_array_t corner_transforms; /**< The array of neighbors of the originating + corner */ } p4est_corner_info_t; +/** Generic interface for transformations between a tree and any of its neighbors */ +typedef struct +{ + p4est_connect_type_t neighbor_type; /**< type of connection to neighbor*/ + p4est_topidx_t neighbor; /**< neighbor tree index */ + int8_t index_self; /**< index of interface from self's + perspective */ + int8_t index_neighbor; /**< index of interface from neighbor's + perspective */ + int8_t perm[P4EST_DIM]; /**< permutation of dimensions when + transforming self coords to + neighbor coords */ + int8_t sign[P4EST_DIM]; /**< sign changes when transforming self + coords to neighbor coords */ + p4est_qcoord_t origin_self[P4EST_DIM]; /**< point on the interface from + self's perspective */ + p4est_qcoord_t origin_neighbor[P4EST_DIM]; /**< point on the interface + from neighbor's + perspective */ +} +p4est_neighbor_transform_t; + +/* *INDENT-OFF* */ + +/** Transform from self's coordinate system to neighbor's coordinate system. + * + * \param [in] nt A neighbor transform. + * \param [in] self_coords Input quadrant coordinates in self coordinates. + * \param [out] neigh_coords Coordinates transformed into neighbor coordinates. + */ +void p4est_neighbor_transform_coordinates + (const p4est_neighbor_transform_t * nt, + const p4est_qcoord_t self_coords[P4EST_DIM], + p4est_qcoord_t neigh_coords[P4EST_DIM]); + +/** Transform from neighbor's coordinate system to self's coordinate system. + * + * \param [in] nt A neighbor transform. + * \param [in] neigh_coords Input quadrant coordinates in self coordinates. + * \param [out] self_coords Coordinates transformed into neighbor coordinates. + */ +void p4est_neighbor_transform_coordinates_reverse + (const p4est_neighbor_transform_t * nt, + const p4est_qcoord_t neigh_coords[P4EST_DIM], + p4est_qcoord_t self_coords[P4EST_DIM]); + +/** Fill an array with the neighbor transforms based on a specific boundary type. + * This function generalizes all other inter-tree transformation objects + * + * \param [in] conn Connectivity structure. + * \param [in] tree_id The number of the tree. + * \param [in] boundary_type The type of the boundary connection (self, face, corner). + * \param [in] boundary_index The index of the boundary. + * \param [in,out] neighbor_transform_array Array of the neighbor transforms. + */ +void p4est_connectivity_get_neighbor_transforms + (p4est_connectivity_t *conn, + p4est_topidx_t tree_id, + p4est_connect_type_t boundary_type, + int boundary_index, + sc_array_t *neighbor_transform_array); + +/* *INDENT-ON* */ + /** Store the corner numbers 0..4 for each tree face. */ extern const int p4est_face_corners[4][2]; @@ -220,8 +325,8 @@ extern const int p4est_child_corner_faces[4][4]; * This version expects the neighbor face and orientation separately. * \param [in] fc A face corner number in 0..1. * \param [in] f A face that the face corner number \a fc is relative to. - * \param [in] nf A neighbor face that is on the other side of \f. - * \param [in] o The orientation between tree boundary faces \a f and \nf. + * \param [in] nf A neighbor face that is on the other side of \a f. + * \param [in] o The orientation between tree boundary faces \a f and \a nf. * \return The face corner number relative to the neighbor's face. */ int p4est_connectivity_face_neighbor_face_corner @@ -231,8 +336,8 @@ int p4est_connectivity_face_neighbor_face_corner * This version expects the neighbor face and orientation separately. * \param [in] c A corner number in 0..3. * \param [in] f A face number that touches the corner \a c. - * \param [in] nf A neighbor face that is on the other side of \f. - * \param [in] o The orientation between tree boundary faces \a f and \nf. + * \param [in] nf A neighbor face that is on the other side of \a f. + * \param [in] o The orientation between tree boundary faces \a f and \a nf. * \return The number of the corner seen from the neighbor tree. */ int p4est_connectivity_face_neighbor_corner @@ -256,9 +361,16 @@ p4est_connectivity_t *p4est_connectivity_new (p4est_topidx_t num_vertices, * \param [in] num_vertices Number of total vertices (i.e. geometric points). * \param [in] num_trees Number of trees in the forest. * \param [in] num_corners Number of tree-connecting corners. + * \param [in] vertices Coordinates of the vertices of the trees. + * \param [in] ttv The tree-to-vertex array. + * \param [in] ttt The tree-to-tree array. + * \param [in] ttf The tree-to-face array (int8_t). + * \param [in] ttc The tree-to-corner array. * \param [in] coff Corner-to-tree offsets (num_corners + 1 values). * This must always be non-NULL; in trivial cases * it is just a pointer to a p4est_topix value of 0. + * \param [in] ctt The corner-to-tree array. + * \param [in] ctc The corner-to-corner array. * \return The connectivity is checked for validity. */ p4est_connectivity_t *p4est_connectivity_new_copy (p4est_topidx_t @@ -362,8 +474,10 @@ int p4est_connectivity_save (const char *filename, p4est_connectivity_t *p4est_connectivity_source (sc_io_source_t * source); /** Create new connectivity from a memory buffer. + * This function aborts on malloc errors. * \param [in] buffer The connectivity is created from this memory buffer. - * \return The newly created connectivity, or NULL on error. + * \return The newly created connectivity, or NULL on format + * error of the buffered connectivity data. */ p4est_connectivity_t *p4est_connectivity_inflate (sc_array_t * buffer); @@ -388,6 +502,19 @@ p4est_connectivity_t *p4est_connectivity_new_periodic (void); */ p4est_connectivity_t *p4est_connectivity_new_rotwrap (void); +/** Create a connectivity structure for an donut-like circle. + * The circle consists of 6 trees connecting each other by their faces. + * The trees are laid out as a hexagon between [-2, 2] in the y direction + * and [-sqrt(3), sqrt(3)] in the x direction. The hexagon has flat + * sides along the y direction and pointy ends in x. + */ +p4est_connectivity_t *p4est_connectivity_new_circle (void); + +/** Create a connectivity structure for a five-trees geometry with a hole. + * The geometry covers the square [0, 3]**2, where the hole is [1, 2]**2. + */ +p4est_connectivity_t *p4est_connectivity_new_drop (void); + /** Create a connectivity structure for two trees being rotated * w.r.t. each other in a user-defined way * \param[in] l_face index of left face @@ -415,9 +542,12 @@ p4est_connectivity_t *p4est_connectivity_new_moebius (void); p4est_connectivity_t *p4est_connectivity_new_star (void); /** Create a connectivity structure for the six sides of a unit cube. - * The ordering of the trees is as follows: 0 1 - * 2 3 <-- 3: axis-aligned top side - * 4 5. + * The ordering of the trees is as follows: + * + * 0 1 + * 2 3 <-- 3: axis-aligned top side + * 4 5 + * * This choice has been made for maximum symmetry (see tree_to_* in .c file). */ p4est_connectivity_t *p4est_connectivity_new_cubed (void); @@ -433,12 +563,14 @@ p4est_connectivity_t *p4est_connectivity_new_disk_nonperiodic (void); * This disk can just as well be used as a square to test non-Cartesian maps. * Without any mapping this connectivity covers the square [-3, 3]**2. * \note The API of this function has changed to accept two arguments. - * You can query the #define P4EST_CONN_DISK_PERIODIC to check + * You can query the \ref P4EST_CONN_DISK_PERIODIC to check * whether the new version with the argument is in effect. * - * The ordering of the trees is as follows: 4 - * 1 2 3 - * 0. + * The ordering of the trees is as follows: + * + * 4 + * 1 2 3 + * 0 * * The outside x faces may be identified topologically. * The outside y faces may be identified topologically. @@ -466,25 +598,25 @@ p4est_connectivity_t *p4est_connectivity_new_disk (int periodic_a, * This connectivity is meant to be used together with \ref p4est_geometry_new_icosahedron * to map the sphere. * - * The flat connectivity looks like that: + * The flat connectivity looks like that. * Vextex numbering: * - * A00 A01 A02 A03 A04 - * / \ / \ / \ / \ / \ - * A05---A06---A07---A08---A09---A10 - * \ / \ / \ / \ / \ / \ - * A11---A12---A13---A14---A15---A16 - * \ / \ / \ / \ / \ / - * A17 A18 A19 A20 A21 + * A00 A01 A02 A03 A04 + * / \ / \ / \ / \ / \ + * A05---A06---A07---A08---A09---A10 + * \ / \ / \ / \ / \ / \ + * A11---A12---A13---A14---A15---A16 + * \ / \ / \ / \ / \ / + * A17 A18 A19 A20 A21 * * Origin in A05. * * Tree numbering: * - * 0 2 4 6 8 - * 1 3 5 7 9 + * 0 2 4 6 8 + * 1 3 5 7 9 */ -p4est_connectivity_t *p4est_connectivity_new_icosahedron (); +p4est_connectivity_t *p4est_connectivity_new_icosahedron (void); /** Create a connectivity structure that builds a 2d spherical shell. * \ref p8est_connectivity_new_shell @@ -498,6 +630,20 @@ p4est_connectivity_t *p4est_connectivity_new_shell2d (void); */ p4est_connectivity_t *p4est_connectivity_new_disk2d (void); +/** Create a connectivity structure that maps a 2d bowtie structure. + * + * The 2 trees are connected by a corner connection at node A3 (0, 0). + * the nodes are given as: + * + * A00 A01 + * / \ / \ + * A02 A03 A04 + * \ / \ / + * A05 A06 + * + */ +p4est_connectivity_t *p4est_connectivity_new_bowtie (void); + /** A rectangular m by n array of trees with configurable periodicity. * The brick is periodic in x and y if periodic_a and periodic_b are true, * respectively. @@ -527,13 +673,13 @@ p4est_connectivity_t *p4est_connectivity_new_byname (const char *name); * than a power of 2. * * \param [in] conn A valid connectivity - * \param [in] num_per_edge The number of new trees in each direction. + * \param [in] num_per_dim The number of new trees in each direction. * Must use no more than \ref P4EST_OLD_QMAXLEVEL bits. * * \return a refined connectivity. */ p4est_connectivity_t *p4est_connectivity_refine (p4est_connectivity_t * conn, - int num_per_edge); + int num_per_dim); /** Fill an array with the axis combination of a face neighbor transform. * \param [in] iface The number of the originating face. @@ -546,7 +692,7 @@ p4est_connectivity_t *p4est_connectivity_refine (p4est_connectivity_t * conn, * the first referring to the tangential and the second * to the normal. A permutation of (0, 1). * [3,5] The coordinate axis sequence of the target face. - * [6,8] Edge reversal flag for tangential axis (boolean); + * [6,8] Face reversal flag for tangential axis (boolean); * face code in [0, 3] for the normal coordinate q: * 0: q' = -q * 1: q' = q + 1 @@ -558,14 +704,16 @@ void p4est_expand_face_transform (int iface, int nface, int ftransform[]); /** Fill an array with the axis combinations of a tree neighbor transform. - * \param [in] itree The number of the originating tree. - * \param [in] iface The number of the originating tree's face. - * \param [out] ftransform This array holds 9 integers. - * [0,2] The coordinate axis sequence of the origin face. - * [3,5] The coordinate axis sequence of the target face. - * [6,8] Edge reverse flag for axis t; face code for axis n. - * [1,4,7] 0 (unused for compatibility with 3D). - * \return The face neighbor tree if it exists, -1 otherwise. + * \param [in] connectivity Connectivity structure. + * \param [in] itree The number of the originating tree. + * \param [in] iface The number of the originating tree's face. + * \param [out] ftransform This array holds 9 integers. + * [0,2] The coordinate axis sequence of the origin face. + * [3,5] The coordinate axis sequence of the target face. + * [6,8] Face reversal flag for axis t; face code for axis n. + * \see p4est_expand_face_transform. + * [1,4,7] 0 (unused for compatibility with 3D). + * \return The face neighbor tree if it exists, -1 otherwise. */ p4est_topidx_t p4est_find_face_transform (p4est_connectivity_t * connectivity, @@ -573,9 +721,10 @@ p4est_topidx_t p4est_find_face_transform (p4est_connectivity_t * int iface, int ftransform[]); /** Fills an array with information about corner neighbors. - * \param [in] itree The number of the originating tree. - * \param [in] icorner The number of the originating corner. - * \param [in,out] ci A p4est_corner_info_t structure with initialized array. + * \param [in] connectivity Connectivity structure. + * \param [in] itree The number of the originating tree. + * \param [in] icorner The number of the originating corner. + * \param [in,out] ci A p4est_corner_info_t structure with initialized array. */ void p4est_find_corner_transform (p4est_connectivity_t * connectivity, @@ -621,6 +770,9 @@ void p4est_connectivity_permute (p4est_connectivity_t * conn, #ifdef P4EST_WITH_METIS /** Reorder a connectivity using METIS. + * + * \note This function is only available if configured successfully `--with-metis`. + * * This function takes a connectivity \a conn and a parameter \a k, * which will typically be the number of processes, and reorders the trees * such that if every processes is assigned (num_trees / k) trees, the @@ -647,6 +799,9 @@ void p4est_connectivity_reorder (sc_MPI_Comm comm, int k, p4est_connect_type_t ctype); /** Reorder a connectivity using METIS. + * + * \note This function is only available if configured successfully `--with-metis`. + * * This is the same form as \ref p4est_connectivity_reorder but it takes an * initialized sc array \a newid as extra argument. * In this way, the users can map old indices to new indices in the case it @@ -737,34 +892,34 @@ p4est_corner_array_index (sc_array_t * array, size_t it) * gives the 4 vertices in 2D (8 vertices in 3D) of each element in counter * clockwise order. So in 2D the nodes are given as: * - * 4 3 - * +-------------------+ - * | | - * | | - * | | - * | | - * | | - * | | - * +-------------------+ - * 1 2 + * 4 3 + * +-------------------+ + * | | + * | | + * | | + * | | + * | | + * | | + * +-------------------+ + * 1 2 * * and in 3D they are given as: * - * 8 7 - * +---------------------+ - * |\ |\ - * | \ | \ - * | \ | \ - * | \ | \ - * | 5+---------------------+6 - * | | | | - * +----|----------------+ | - * 4\ | 3 \ | - * \ | \ | - * \ | \ | - * \| \| - * +---------------------+ - * 1 2 + * 8 7 + * +---------------------+ + * |\ |\ + * | \ | \ + * | \ | \ + * | \ | \ + * | 5+---------------------+6 + * | | | | + * +----|----------------+ | + * 4\ | 3 \ | + * \ | \ | + * \ | \ | + * \| \| + * +---------------------+ + * 1 2 * * \code * *Heading @@ -826,34 +981,34 @@ int p4est_connectivity_read_inp_stream (FILE * stream, * gives the 4 vertices in 2D (8 vertices in 3D) of each element in counter * clockwise order. So in 2D the nodes are given as: * - * 4 3 - * +-------------------+ - * | | - * | | - * | | - * | | - * | | - * | | - * +-------------------+ - * 1 2 + * 4 3 + * +-------------------+ + * | | + * | | + * | | + * | | + * | | + * | | + * +-------------------+ + * 1 2 * * and in 3D they are given as: * - * 8 7 - * +---------------------+ - * |\ |\ - * | \ | \ - * | \ | \ - * | \ | \ - * | 5+---------------------+6 - * | | | | - * +----|----------------+ | - * 4\ | 3 \ | - * \ | \ | - * \ | \ | - * \| \| - * +---------------------+ - * 1 2 + * 8 7 + * +---------------------+ + * |\ |\ + * | \ | \ + * | \ | \ + * | \ | \ + * | 5+---------------------+6 + * | | | | + * +----|----------------+ | + * 4\ | 3 \ | + * \ | \ | + * \ | \ | + * \| \| + * +---------------------+ + * 1 2 * * \code * *Heading diff --git a/src/p4est_extended.h b/src/p4est_extended.h index a6dc3098c..3ee30f7d7 100644 --- a/src/p4est_extended.h +++ b/src/p4est_extended.h @@ -40,10 +40,10 @@ #ifndef P4EST_EXTENDED_H #define P4EST_EXTENDED_H -#include #include #include #include +#include SC_EXTERN_C_BEGIN; @@ -134,7 +134,7 @@ int p4est_lid_is_equal (const p4est_lid_t * a, /** Initializes an unsigned 64 bit integer. \a high is just a * a placeholder to use the same interface in 3D. - * \param [in,out] input A pointer to a p4est_lid_t that will be intialized. + * \param [in,out] input A pointer to a p4est_lid_t that will be initialized. * \param [in] high The given high bits must be zero. * \param [in] low The given low bits to initialize \a input. */ @@ -142,17 +142,17 @@ void p4est_lid_init (p4est_lid_t * input, uint64_t high, uint64_t low); /** Initializes a linear index to zero. - * \param [out] input A pointer to a p4est_lid_t that will be intialized. + * \param [out] input A pointer to a p4est_lid_t that will be initialized. */ void p4est_lid_set_zero (p4est_lid_t * input); /** Initializes a linear index to one. - * \param [out] input A pointer to a p4est_lid_t that will be intialized. + * \param [out] input A pointer to a p4est_lid_t that will be initialized. */ void p4est_lid_set_one (p4est_lid_t * input); /** Initializes a linear index to an unsigned 64 bit integer. - * \param [out] input A pointer to a p4est_lid_t that will be intialized. + * \param [out] input A pointer to a p4est_lid_t that will be initialized. */ void p4est_lid_set_uint64 (p4est_lid_t * input, uint64_t u); @@ -198,7 +198,7 @@ void p4est_lid_add (const p4est_lid_t * a, const p4est_lid_t * b, p4est_lid_t * result); -/** Substracts the p4est_lid_t \a b from the p4est_lid_t \a a. +/** Subtracts the p4est_lid_t \a b from the p4est_lid_t \a a. * This function assumes that the result is >= 0. * \a result == \a a or \a result == \a b is not allowed. * \a a == \a b is allowed. @@ -284,7 +284,7 @@ void p4est_lid_shift_left (const p4est_lid_t * input, void p4est_lid_add_inplace (p4est_lid_t * a, const p4est_lid_t * b); -/** Substracts the uint128_t \a b from the uint128_t \a a. +/** Subtracts the uint128_t \a b from the uint128_t \a a. * The result is saved in \a a. \a a == \a b is allowed. * This function assumes that the result is >= 0. * \param [in,out] a A pointer to a p4est_lid_t. @@ -382,6 +382,8 @@ p4est_t *p4est_new_ext (sc_MPI_Comm mpicomm, void *user_pointer); /** Create a new mesh. + * This function sets a subset of the mesh creation parameters. For full control + * use \ref p4est_mesh_new_params. * \param [in] p4est A forest that is fully 2:1 balanced. * \param [in] ghost The ghost layer created from the * provided p4est. @@ -389,8 +391,8 @@ p4est_t *p4est_new_ext (sc_MPI_Comm mpicomm, * compute the quad_to_tree list. * \param [in] compute_level_lists Boolean to decide whether to compute the * level lists in quad_level. - * \param [in] btype Currently ignored, only face neighbors - * are stored. + * \param [in] btype Flag indicating the connection types (face, + corner) stored in the mesh. * \return A fully allocated mesh structure. */ p4est_mesh_t *p4est_mesh_new_ext (p4est_t * p4est, @@ -590,6 +592,40 @@ p4est_t *p4est_source_ext (sc_io_source_t * src, int broadcasthead, void *user_pointer, p4est_connectivity_t ** connectivity); +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +/** Open a file for reading without knowing the p4est that is associated + * with the mesh-related data in the file (cf. \ref p4est_file_open_read). + * For more general comments on open_read see the documentation of + * \ref p4est_file_open_read. + * The parameters that are not documented are the same as in \ref + * p4est_file_open_read. + * + * \param [in] mpicomm The MPI communicator that is used to read the file. + */ +p4est_file_context_t *p4est_file_open_read_ext (sc_MPI_Comm mpicomm, + const char *filename, + char *user_string, + p4est_gloidx_t * + global_num_quadrants, + int *errcode); + +/** Read a data field and specify the partition for reading in parallel. + * See also the documentation of \ref p4est_file_read_field. + * + * \param [in] gfq An array of the size mpisize + 1 that contains the global + * first quadrants per rank and + * gfq[mpisize] == global_num_quadrants. This defines + * partition that is used to read the data field in parallel. + */ +p4est_file_context_t *p4est_file_read_field_ext (p4est_file_context_t * fc, + p4est_gloidx_t * gfq, + size_t quadrant_size, + sc_array_t * quadrant_data, + char *user_string, + int *errcode); +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + /** Create the data necessary to create a PETsc DMPLEX representation of a * forest, as well as the accompanying lnodes and ghost layer. The forest * must be at least face balanced (see p4est_balance()). See diff --git a/src/p4est_geometry.c b/src/p4est_geometry.c index 5e2963dae..fd47a467c 100644 --- a/src/p4est_geometry.c +++ b/src/p4est_geometry.c @@ -24,7 +24,7 @@ /** * \file p4est_geometry.c - * We provide the identity transformation for reference. + * We provide several transformations for reference. * Please implement p4est_geometry_t as you see fit. */ @@ -42,6 +42,7 @@ typedef enum P4EST_GEOMETRY_BUILTIN_ICOSAHEDRON, P4EST_GEOMETRY_BUILTIN_SHELL2D, P4EST_GEOMETRY_BUILTIN_DISK2D, + P4EST_GEOMETRY_BUILTIN_SPHERE2D, P4EST_GEOMETRY_LAST } p4est_geometry_builtin_type_t; @@ -70,6 +71,13 @@ typedef struct p4est_geometry_builtin_disk2d } p4est_geometry_builtin_disk2d_t; +typedef struct p4est_geometry_builtin_sphere2d +{ + p4est_geometry_builtin_type_t type; + double R; +} +p4est_geometry_builtin_sphere2d_t; + typedef struct p4est_geometry_builtin { /** The geom member needs to come first; we cast to p4est_geometry_t * */ @@ -80,6 +88,7 @@ typedef struct p4est_geometry_builtin p4est_geometry_builtin_icosahedron_t icosahedron; p4est_geometry_builtin_shell2d_t shell2d; p4est_geometry_builtin_disk2d_t disk2d; + p4est_geometry_builtin_sphere2d_t sphere2d; } p; } @@ -98,12 +107,14 @@ p4est_geometry_destroy (p4est_geometry_t * geom) } } -static void +void p4est_geometry_connectivity_X (p4est_geometry_t * geom, p4est_topidx_t which_tree, const double abc[3], double xyz[3]) { + P4EST_ASSERT (geom->user != NULL); p4est_connectivity_t *connectivity = (p4est_connectivity_t *) geom->user; + P4EST_ASSERT (connectivity->tree_to_vertex != NULL); const p4est_topidx_t *tree_to_vertex = connectivity->tree_to_vertex; const double *v = connectivity->vertices; double eta_x, eta_y, eta_z = 0.; @@ -118,7 +129,9 @@ p4est_geometry_connectivity_X (p4est_geometry_t * geom, /* these are reference coordinates in [0, 1]**d */ eta_x = abc[0]; eta_y = abc[1]; +#ifdef P4_TO_P8 eta_z = abc[2]; +#endif /* bi/trilinear transformation */ for (j = 0; j < 3; ++j) { @@ -157,7 +170,19 @@ p4est_geometry_new_connectivity (p4est_connectivity_t * conn) #ifndef P4_TO_P8 -/* geometric coordinate transformation */ +/** + * Geometric coordinate transformation for icosahedron geometry. + * + * Define the geometric transformation from tree-local reference coordinates + * to physical space + * + * \param[in] geom associated geometry + * \param[in] which_tree tree id inside forest + * \param[in] rst tree-local reference coordinates : [0,1]^2. + * Note: rst[2] is never accessed + * \param[out] xyz Cartesian coordinates in physical space after geometry + * + */ static void p4est_geometry_icosahedron_X (p4est_geometry_t * geom, p4est_topidx_t which_tree, @@ -179,7 +204,7 @@ p4est_geometry_icosahedron_X (p4est_geometry_t * geom, eta_y = rst[1]; /* - * icosahedron node cartesian coordinates + * icosahedron node Cartesian coordinates * used for mapping connectivity vertices to 3D nodes. */ const double N[12 * 3] = { @@ -242,7 +267,7 @@ p4est_geometry_icosahedron_X (p4est_geometry_t * geom, const int i2 = tree_to_nodes[which_tree * 4 + 2]; const int i3 = tree_to_nodes[which_tree * 4 + 3]; - /* get 3D cartesian coordinates of our face */ + /* get 3D Cartesian coordinates of our face */ const double n0[3] = { N[i0 * 3 + 0], N[i0 * 3 + 1], N[i0 * 3 + 2] }; const double n1[3] = @@ -312,7 +337,19 @@ p4est_geometry_new_icosahedron (p4est_connectivity_t * conn, double R) } /* p4est_geometry_new_icosahedron */ -/* geometric coordinate transformation */ +/** + * Geometric coordinate transformation for shell2d geometry. + * + * Define the geometric transformation from tree-local reference coordinates + * to physical space. + * + * \param[in] geom associated geometry + * \param[in] which_tree tree id inside forest + * \param[in] rst tree-local reference coordinates : [0,1]^2. + * Note: rst[2] is never accessed + * \param[out] xyz Cartesian coordinates in physical space after geometry + * + */ static void p4est_geometry_shell2d_X (p4est_geometry_t * geom, p4est_topidx_t which_tree, @@ -393,15 +430,16 @@ p4est_geometry_new_shell2d (p4est_connectivity_t * conn, double R2, double R1) /** * geometric coordinate transformation for disk2d geometry. * - * Define the geometric transformation from logical space (where AMR - * is performed) to the physical space. + * Define the geometric transformation from tree-local reference coordinates + * to physical space. * - * \param[in] p4est the forest + * \param[in] geom associated geometry * \param[in] which_tree tree id inside forest - * \param[in] rst coordinates in AMR space : [0,1]^3 - * \param[out] xyz cartesian coordinates in physical space after geometry + * \param[in] rst tree-local reference coordinates : [0,1]^2. + * Note: rst[2] is never accessed. + * \param[out] xyz Cartesian coordinates in physical space after geometry * - * Note abc[3] contains cartesian coordinates in logical + * Note abc[3] contains Cartesian coordinates in logical * vertex space (before geometry). */ static void @@ -450,7 +488,7 @@ p4est_geometry_disk2d_X (p4est_geometry_t * geom, R = disk2d->R0sqrbyR1 * pow (disk2d->R1byR0, abc[1]); /* R*cos(theta) */ - //q = R / sqrt (x * x + 1.); + /* q = R / sqrt (x * x + 1.); */ q = R / sqrt (1. + (1. - p) * (tanx * tanx) + 1. * p); /* assign correct coordinates based on patch id */ @@ -507,7 +545,7 @@ p4est_geometry_new_disk2d (p4est_connectivity_t * conn, double R0, double R1) /* variables useful for the center square */ disk2d->Clength = R0 / sqrt (2.); - //disk2d->CdetJ = pow (R0 / sqrt (3.), 3.); + /* disk2d->CdetJ = pow (R0 / sqrt (3.), 3.); */ builtin->geom.name = "p4est_disk2d"; builtin->geom.user = conn; @@ -517,4 +555,64 @@ p4est_geometry_new_disk2d (p4est_connectivity_t * conn, double R0, double R1) } /* p4est_geometry_new_disk2d */ +/** + * geometric coordinate transformation for sphere2d geometry. + * + * Define the geometric transformation from tree-local reference coordinates to the + * physical space. + * + * \param[in] geom associated geometry + * \param[in] which_tree tree id inside forest + * \param[in] rst tree-local reference coordinates : [0,1]^2. + * Note: rst[2] is never accessed + * \param[out] xyz Cartesian coordinates in physical space after geometry + * + */ +static void +p4est_geometry_sphere2d_X (p4est_geometry_t * geom, + p4est_topidx_t which_tree, + const double rst[3], double xyz[3]) +{ + const struct p4est_geometry_builtin_sphere2d *sphere2d + = &((p4est_geometry_builtin_t *) geom)->p.sphere2d; + double R; + + /* transform from the tree-local reference coordinates into the cube-surface + * in physical space using vertex bi/trilinear transformation. + */ + p4est_geometry_connectivity_X (geom, which_tree, rst, xyz); + + /* align cube center with origin */ + xyz[0] -= 0.5; + xyz[1] -= 0.5; + xyz[2] -= 0.5; + + /* normalise to radius R sphere */ + R = sphere2d->R; + double R_on_norm = + R / sqrt (xyz[0] * xyz[0] + xyz[1] * xyz[1] + xyz[2] * xyz[2]); + xyz[0] *= R_on_norm; + xyz[1] *= R_on_norm; + xyz[2] *= R_on_norm; +} /* p4est_geometry_sphere2d_X */ + +p4est_geometry_t * +p4est_geometry_new_sphere2d (p4est_connectivity_t * conn, double R) +{ + p4est_geometry_builtin_t *builtin; + struct p4est_geometry_builtin_sphere2d *sphere2d; + + builtin = P4EST_ALLOC_ZERO (p4est_geometry_builtin_t, 1); + + sphere2d = &builtin->p.sphere2d; + sphere2d->type = P4EST_GEOMETRY_BUILTIN_SPHERE2D; + sphere2d->R = R; + + builtin->geom.name = "p4est_sphere2d"; + builtin->geom.user = conn; + builtin->geom.X = p4est_geometry_sphere2d_X; + + return (p4est_geometry_t *) builtin; +} /* p4est_geometry_new_sphere2d */ + #endif /* !P4_TO_P8 */ diff --git a/src/p4est_geometry.h b/src/p4est_geometry.h index af01fd683..c92da6d7a 100644 --- a/src/p4est_geometry.h +++ b/src/p4est_geometry.h @@ -22,7 +22,18 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** \file p4est_geometry.h transforms from vertex frame to physical space. +/** \file p4est_geometry.h Transform from tree-local "reference" coordinate system + * to global "physical space" coordinates. These are used in \ref p4est_vtk.h to write + * global coordinate meshes to disk. + * + * We provide several example geometries for use. You may also implement your own + * geometry as you see fit. + * + * \note For geometry purposes, each tree has the local coordinate system + * \f$[0,1]^d\f$. For legacy/\ref p8est compatibility reasons the local + * coordinates are always represented as a triple abc[3]. + * For a 2D quadtree mesh the local coordinates are abc[0] and abc[1] and + * the third coordinate abc[2] should be ignored. * * \ingroup p4est */ @@ -37,23 +48,38 @@ SC_EXTERN_C_BEGIN; /** This object encapsulates a custom geometry transformation. */ typedef struct p4est_geometry p4est_geometry_t; -/** Forward transformation from the reference unit square to physical space. - * Note that the two-dimensional connectivities have 3D vertex coordinates - * that can be used in the transformation if so desired. - * The physical space "xyz" is user-defined, currently used for VTK output. +/** Forward transformation from the tree-local coordinates to physical space. + * + * \note The two-dimensional connectivities built into p4est have 3D vertex coordinates + * that can be used in the transformation if so desired. However, connectivities are + * not in general required to have vertex coordinate information. + * + * \param[in] geom associated geometry + * \param[in] which_tree tree id inside forest + * \param[in] abc tree-local coordinates: \f$[0,1]^d\f$. + * For 2D meshes abc[2] should never be accessed. + * \param[out] xyz cartesian coordinates in physical space after geometry */ typedef void (*p4est_geometry_X_t) (p4est_geometry_t * geom, p4est_topidx_t which_tree, const double abc[3], double xyz[3]); -/** Destructor prototype for a user-allocated \a p4est_geometry_t. - * It is invoked by p4est_geometry_destroy. If the user chooses to - * reserve the structure statically, simply don't call p4est_geometry_destroy. +/** Destructor prototype for a user-allocated \ref p4est_geometry_t. + * It is invoked by \ref p4est_geometry_destroy. If the user chooses to + * reserve the structure statically, there is no need to provide it. */ typedef void (*p4est_geometry_destroy_t) (p4est_geometry_t * geom); -/** This structure can be filled or allocated by the user. +/** Encapsulates a custom transformation from tree-local coordinates to + * user defined physical space. + * + * Used in \ref p4est_vtk.h to write global-coordinate meshes. + * + * Some internal p4est functions assume that *user points to a + * \ref p4est_connectivity. However, in general it can be used as the user wishes. + * + * This structure can be filled or allocated by the user. * p4est will never change its contents. */ struct p4est_geometry @@ -62,51 +88,90 @@ struct p4est_geometry void *user; /**< User's choice is arbitrary. */ p4est_geometry_X_t X; /**< Coordinate transformation. */ p4est_geometry_destroy_t destroy; /**< Destructor called by - p4est_geometry_destroy. If + \ref p4est_geometry_destroy. If NULL, P4EST_FREE is called. */ }; /** Can be used to conveniently destroy a geometry structure. * The user is free not to call this function at all if they handle the - * memory of the p4est_geometry_t in their own way. + * memory of the \ref p4est_geometry_t in their own way. */ void p4est_geometry_destroy (p4est_geometry_t * geom); /** Create a geometry structure based on the vertices in a connectivity. * The transformation is constructed using bilinear interpolation. - * \param [in] conn A p4est_connectivity_t with valid vertices. We do NOT - * take ownership and expect this structure to stay alive. - * \return Geometry structure; use with p4est_geometry_destroy. + * \param [in] conn A connectivity with vertex coordinate information. + * We do \a not take ownership and expect this structure to stay alive. + * \return Geometry structure; use with \ref p4est_geometry_destroy. */ p4est_geometry_t *p4est_geometry_new_connectivity (p4est_connectivity_t * conn); -/** Create a geometry for mapping the 3d sphere using 2d connectivity icosahedron. +/** Geometric coordinate transformation for geometry created with + * \ref p4est_geometry_new_connectivity. This is defined by + * tri/binlinear interpolation from vertex coordinates. + * + * May also be used as a building block in custom geometric coordinate transforms. + * See for example \ref p4est_geometry_sphere2d_X or \ref p4est_geometry_disk2d_X. + * + * \param[in] geom associated geometry + * \param[in] which_tree tree id inside forest + * \param[in] abc tree-local reference coordinates : [0,1]^3. + * Note: abc[2] is only accessed by the P4_TO_P8 version + * \param[out] xyz Cartesian coordinates in physical space after geometry * + * \warning The associated geometry is assumed to have a connectivity + * as its *user field, and this connectivity is assumed to have vertex + * information in its *tree_to_vertex field. + */ +void p4est_geometry_connectivity_X (p4est_geometry_t * geom, + p4est_topidx_t which_tree, + const double abc[3], double xyz[3]); + +/** Create a geometry for mapping the sphere using 2d connectivity icosahedron. + * + * \param[in] conn The result of \ref p4est_connectivity_new_icosahedron. + * \param[in] R The radius of the sphere. */ p4est_geometry_t *p4est_geometry_new_icosahedron (p4est_connectivity_t * conn, double R); -/** Create a geometry for mapping 2d shell. +/** Create a geometry for mapping the annulus. * This a direct adaptation of geometric shell in 3d. + * + * \param[in] conn The result of \ref p4est_connectivity_new_shell2d. + * \param[in] R1 radius of the inner circle (internal border). + * \param[in] R2 radius of the outer circle (external border). */ p4est_geometry_t *p4est_geometry_new_shell2d (p4est_connectivity_t * conn, double R2, double R1); /** - * disk2d geometry associated to disk2d connectivity. - * - * \param[in] R0 radius of the inner circle - * \param[in] R1 radius of the outer circle (external border) + * Create disk2d geometry associated to disk2d connectivity. * + * \param[in] conn The result of \ref p4est_connectivity_new_disk2d. + * \param[in] R0 radius of the inner circle. + * \param[in] R1 radius of the outer circle (external border). * * This geometry is meant to be used with the disk2d connectivity, - * \ref p4est_connectivity_new_disk2d which is a 5-tree connectivty - * to map the disk. + * which is a 5-tree connectivity to map the spherical disk. */ p4est_geometry_t *p4est_geometry_new_disk2d (p4est_connectivity_t * conn, double R0, double R1); +/** + * Create sphere geometry associated to cubed connectivity. + * + * \param[in] conn The result of \ref p4est_connectivity_new_cubed. + * \param[in] R radius of the sphere + * + * This geometry is meant to be used with the cubed connectivity + * \ref p4est_connectivity_new_cubed, which is a 6-tree connectivity, + * to map the sphere. + */ +p4est_geometry_t *p4est_geometry_new_sphere2d (p4est_connectivity_t * conn, + double R); + SC_EXTERN_C_END; #endif /* !P4EST_GEOMETRY_H */ diff --git a/src/p4est_ghost.c b/src/p4est_ghost.c index de1e5dcea..b1eb48f4f 100644 --- a/src/p4est_ghost.c +++ b/src/p4est_ghost.c @@ -76,6 +76,43 @@ p4est_ghost_array_index (sc_array_t * array, int i) #endif +p4est_ghost_t * +p4est_ghost_new_local (p4est_t * p4est, p4est_connect_type_t ctype) +{ + p4est_ghost_t *ghost; + p4est_topidx_t ntpo; + int Ppo; + + /* assert validity of input parameters */ + P4EST_ASSERT (p4est != NULL); + P4EST_ASSERT (p4est_is_valid (p4est)); + P4EST_ASSERT (P4EST_CONNECT_SELF <= ctype && ctype <= P4EST_CONNECT_FULL); + + /* leave mirror_proc_mirrors and mirror_proc_front* at NULL */ + ghost = P4EST_ALLOC_ZERO (p4est_ghost_t, 1); + + /* ghost meta information */ + Ppo = (ghost->mpisize = p4est->mpisize) + 1; + ntpo = (ghost->num_trees = p4est->connectivity->num_trees) + 1; + ghost->btype = ctype; + + /* the ghost and mirror quadrants themselves */ + sc_array_init (&ghost->ghosts, sizeof (p4est_quadrant_t)); + sc_array_init (&ghost->mirrors, sizeof (p4est_quadrant_t)); + + /* offsets into ghosts and mirrors grouped by tree */ + ghost->tree_offsets = P4EST_ALLOC_ZERO (p4est_locidx_t, ntpo); + ghost->mirror_tree_offsets = P4EST_ALLOC_ZERO (p4est_locidx_t, ntpo); + + /* offsets into ghosts and mirrors grouped by process */ + ghost->proc_offsets = P4EST_ALLOC_ZERO (p4est_locidx_t, Ppo); + ghost->mirror_proc_offsets = P4EST_ALLOC_ZERO (p4est_locidx_t, Ppo); + + /* this ghost layer is valid */ + P4EST_ASSERT (p4est_ghost_is_valid (p4est, ghost)); + return ghost; +} + static p4est_ghost_t *p4est_ghost_new_check (p4est_t * p4est, p4est_connect_type_t btype, p4est_ghost_tolerance_t tol); @@ -231,7 +268,7 @@ p4est_quadrant_find_tree_corner_owners (p4est_t * p4est, static int p4est_ghost_check_range (p4est_ghost_t * ghost, int which_proc, p4est_topidx_t which_tree, - size_t * pstart, size_t * pended) + size_t *pstart, size_t *pended) { size_t start = 0; size_t ended = ghost->ghosts.elem_count; @@ -378,8 +415,7 @@ p4est_quadrant_exists (p4est_t * p4est, p4est_ghost_t * ghost, *(int *) sc_array_push (rproc_arr) = qproc; } if (rquad_arr != NULL) { - rquad = (p4est_quadrant_t *) sc_array_push (rquad_arr); - *rquad = *q; + rquad = p4est_quadrant_array_push_copy (rquad_arr, q); rquad->p.piggy3.which_tree = treeid; rquad->p.piggy3.local_num = (p4est_locidx_t) lnid; } @@ -488,8 +524,7 @@ p4est_quadrant_exists (p4est_t * p4est, p4est_ghost_t * ghost, *(int *) sc_array_push (rproc_arr) = qproc; } if (rquad_arr != NULL) { - rquad = (p4est_quadrant_t *) sc_array_push (rquad_arr); - *rquad = tq; + rquad = p4est_quadrant_array_push_copy (rquad_arr, &tq); rquad->p.piggy3.which_tree = tqtreeid; rquad->p.piggy3.local_num = (p4est_locidx_t) lnid; } @@ -532,8 +567,7 @@ p4est_quadrant_exists (p4est_t * p4est, p4est_ghost_t * ghost, *(int *) sc_array_push (rproc_arr) = qproc; } if (rquad_arr != NULL) { - rquad = (p4est_quadrant_t *) sc_array_push (rquad_arr); - *rquad = tq; + rquad = p4est_quadrant_array_push_copy (rquad_arr, &tq); rquad->p.piggy3.which_tree = tqtreeid; rquad->p.piggy3.local_num = (p4est_locidx_t) lnid; } @@ -642,60 +676,15 @@ p4est_face_quadrant_exists (p4est_t * p4est, p4est_ghost_t * ghost, } } -/** Checks if a quadrant's face is on the boundary of the forest. - * - * \param [in] p4est The forest in which to search for \a q - * \param [in] treeid The tree to which \a q belongs. - * \param [in] q The quadrant that is in question. - * \param [in] face The face of the quadrant that is in question. - * - * \return true if the quadrant's face is on the boundary of the forest and - * false otherwise. - */ -static int -p4est_quadrant_on_face_boundary (p4est_t * p4est, p4est_topidx_t treeid, - int face, const p4est_quadrant_t * q) -{ - p4est_qcoord_t dh, xyz; - p4est_connectivity_t *conn = p4est->connectivity; - - P4EST_ASSERT (0 <= face && face < P4EST_FACES); - P4EST_ASSERT (p4est_quadrant_is_valid (q)); - - if (conn->tree_to_tree[P4EST_FACES * treeid + face] != treeid || - (int) conn->tree_to_face[P4EST_FACES * treeid + face] != face) { - return 0; - } - - dh = P4EST_LAST_OFFSET (q->level); - switch (face / 2) { - case 0: - xyz = q->x; - break; - case 1: - xyz = q->y; - break; -#ifdef P4_TO_P8 - case 2: - xyz = q->z; - break; -#endif - default: - SC_ABORT_NOT_REACHED (); - break; - } - return xyz == ((face & 0x01) ? dh : 0); -} - /** Get the smallest corner neighbor of \a q. * * Gets the smallest corner neighbor, which is half of the size assuming the - * 2-1 constaint. + * 2-1 constraint. * * \param [in] q The quadrant whose corner neighbor will be constructed. * \param [in] corner The corner across which to generate the neighbor. * \param [out] n0 Filled with the smallest corner neighbor, which is - * half of the size assuming the 2-1 constaint. + * half of the size assuming the 2-1 constraint. * \param [out] n0ur If not NULL, it is filled with smallest quadrant * that fits in the upper right corner of \a n0. */ @@ -1194,8 +1183,7 @@ p4est_add_ghost_to_buf (sc_array_t * buf, p4est_topidx_t treeid, } } - qnew = p4est_quadrant_array_push (buf); - *qnew = *q; + qnew = p4est_quadrant_array_push_copy (buf, q); /* Cram the tree id and the local number into the user_data pointer */ qnew->p.piggy3.which_tree = treeid; @@ -1261,8 +1249,7 @@ p4est_ghost_mirror_add (p4est_ghost_mirror_t * m, p4est_topidx_t treeid, if (!m->known) { /* add this quadrant to the mirror array */ - qnew = p4est_quadrant_array_push (m->mirrors); - *qnew = *q; + qnew = p4est_quadrant_array_push_copy (m->mirrors, q); /* cram the tree id and the local number into the user_data pointer */ qnew->p.piggy3.which_tree = treeid; @@ -2072,10 +2059,10 @@ p4est_ghost_new_check (p4est_t * p4est, p4est_connect_type_t btype, /* Wait for the counts */ if (num_peers > 0) { - mpiret = MPI_Waitall (num_peers, recv_request, MPI_STATUSES_IGNORE); + mpiret = sc_MPI_Waitall (num_peers, recv_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); - mpiret = MPI_Waitall (num_peers, send_request, MPI_STATUSES_IGNORE); + mpiret = sc_MPI_Waitall (num_peers, send_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); } @@ -2143,10 +2130,12 @@ p4est_ghost_new_check (p4est_t * p4est, p4est_connect_type_t btype, /* Wait for everything */ if (num_peers > 0) { - mpiret = MPI_Waitall (num_peers, recv_load_request, MPI_STATUSES_IGNORE); + mpiret = + sc_MPI_Waitall (num_peers, recv_load_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); - mpiret = MPI_Waitall (num_peers, send_load_request, MPI_STATUSES_IGNORE); + mpiret = + sc_MPI_Waitall (num_peers, send_load_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); } @@ -2778,8 +2767,7 @@ p4est_ghost_expand_insert (p4est_quadrant_t * q, p4est_topidx_t t, /* add to mirrors */ P4EST_ASSERT (p4est_quadrant_is_valid (q)); - qp = (p4est_quadrant_t *) sc_array_push (send_buf); - P4EST_QUADRANT_INIT (qp); + qp = p4est_quadrant_array_push (send_buf); qp->x = q->x; qp->y = q->y; #ifdef P4_TO_P8 @@ -2796,7 +2784,7 @@ p4est_ghost_expand_insert (p4est_quadrant_t * q, p4est_topidx_t t, P4EST_ASSERT (q->p.piggy3.which_tree == t); qp->p.piggy3.local_num = q->p.piggy3.local_num; - qp2 = (p4est_quadrant_t *) sc_array_push (from_buf); + qp2 = p4est_quadrant_array_push (from_buf); qp2->x = q->x; qp2->y = q->y; #ifdef P4_TO_P8 @@ -2903,7 +2891,7 @@ p4est_ghost_expand_kernel (p4est_topidx_t t, p4est_quadrant_t * mq, } } - /* now create the approriate neighbor and test for overlaps */ + /* now create the appropriate neighbor and test for overlaps */ if (btype == P4EST_CONNECT_FACE) { nnt = p4est_quadrant_face_neighbor_extra (p, nt, point, &np, NULL, conn); @@ -3547,10 +3535,10 @@ p4est_ghost_expand_internal (p4est_t * p4est, p4est_lnodes_t * lnodes, /* Wait for the counts */ if (num_peers > 0) { - mpiret = MPI_Waitall (num_peers, recv_request, MPI_STATUSES_IGNORE); + mpiret = sc_MPI_Waitall (num_peers, recv_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); - mpiret = MPI_Waitall (num_peers, send_request, MPI_STATUSES_IGNORE); + mpiret = sc_MPI_Waitall (num_peers, send_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); } @@ -3628,10 +3616,12 @@ p4est_ghost_expand_internal (p4est_t * p4est, p4est_lnodes_t * lnodes, /* Wait for everything */ if (num_peers > 0) { - mpiret = MPI_Waitall (num_peers, recv_load_request, MPI_STATUSES_IGNORE); + mpiret = + sc_MPI_Waitall (num_peers, recv_load_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); - mpiret = MPI_Waitall (num_peers, send_load_request, MPI_STATUSES_IGNORE); + mpiret = + sc_MPI_Waitall (num_peers, send_load_request, MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); } @@ -3737,13 +3727,12 @@ p4est_ghost_expand_internal (p4est_t * p4est, p4est_lnodes_t * lnodes, if (idx2 < 0) { /* if the target doesn't already know about it, put it in send_bufs * */ - p4est_quadrant_t *q3, *q4; + p4est_quadrant_t *q3; q3 = p4est_quadrant_array_index (mirrors, (size_t) idx); P4EST_ASSERT (p4est_quadrant_is_equal_piggy (q2, q3)); buf = (sc_array_t *) sc_array_index_int (send_bufs, target); - q4 = (p4est_quadrant_t *) sc_array_push (buf); - *q4 = *q3; + (void) p4est_quadrant_array_push_copy (buf, q3); } } else { @@ -4056,13 +4045,12 @@ p4est_ghost_is_valid (p4est_t * p4est, p4est_ghost_t * ghost) for (jl = proc_offset; jl < proc_offset + count; jl++) { p4est_locidx_t idx; - p4est_quadrant_t *q1, *q2; + p4est_quadrant_t *q1; idx = ghost->mirror_proc_mirrors[jl]; q1 = p4est_quadrant_array_index (&ghost->mirrors, (size_t) idx); - q2 = p4est_quadrant_array_push (workspace); - *q2 = *q1; + (void) p4est_quadrant_array_push_copy (workspace, q1); } checksums_send[i] = diff --git a/src/p4est_ghost.h b/src/p4est_ghost.h index 5c74baa80..a0971df4e 100644 --- a/src/p4est_ghost.h +++ b/src/p4est_ghost.h @@ -131,6 +131,17 @@ int p4est_quadrant_find_owner (p4est_t * p4est, p4est_ghost_t *p4est_ghost_new (p4est_t * p4est, p4est_connect_type_t btype); +/** Generate an empty ghost layer. + * This ghost layer pretends that there are no parallel neighbor elements. + * It is useful if general algorithms should be run with local data only. + * \param [in] p4est Valid forest. + * \param [in] ctype Ghosts to include (none, across face, face/corner). + * This variable must be valid but has no effect. + * \return Valid ghost layer of zero ghost elements. + */ +p4est_ghost_t *p4est_ghost_new_local (p4est_t * p4est, + p4est_connect_type_t ctype); + /** Frees all memory used for the ghost layer. */ void p4est_ghost_destroy (p4est_ghost_t * ghost); diff --git a/src/p4est_io.c b/src/p4est_io.c index 39860aa27..7d13a3881 100644 --- a/src/p4est_io.c +++ b/src/p4est_io.c @@ -34,6 +34,141 @@ #include #endif #include +#include + +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +#define P4EST_FILE_COMPRESSED_QUAD_SIZE ((P4EST_DIM + 1) *\ + sizeof (p4est_qcoord_t)) + /**< size of a compressed quadrant */ + +/* error checking macros for p4est_file functions */ + +#define P4EST_FILE_IS_SUCCESS(errcode) ((errcode == sc_MPI_SUCCESS)\ + || (errcode == P4EST_FILE_ERR_SUCCESS)) + +/** Examine the p4est file return value and print an error if there is one. + * The message passed is appended to p4est file, file and line information. + */ +#define P4EST_FILE_CHECK_VERBOSE(errcode,user_msg) do { \ + char p4est_msg[sc_MPI_MAX_ERROR_STRING]; \ + int p4est_msglen; \ + if (!P4EST_FILE_IS_SUCCESS (errcode)) { \ + p4est_file_error_code (errcode, &errcode); \ + p4est_file_error_string (errcode, p4est_msg, &p4est_msglen); \ + SC_GLOBAL_LERRORF ("%s at %s:%d: %s\n", \ + (user_msg), __FILE__, __LINE__, p4est_msg);\ + }} while (0) + +/** This macro performs a clean up in the case of a MPI I/O open error. + * We make use of the fact that sc_mpi_open is always called collectively. + */ +#define P4EST_FILE_CHECK_OPEN(errcode, fc, user_msg, cperrcode) do {\ + P4EST_FILE_CHECK_VERBOSE (errcode, user_msg);\ + *cperrcode = errcode; \ + if (!P4EST_FILE_IS_SUCCESS (errcode)) { \ + p4est_file_error_cleanup (&fc->file); \ + P4EST_FREE (fc); \ + p4est_file_error_code (errcode, cperrcode); \ + return NULL;}} while (0) + +/** The same as \ref P4EST_FILE_CHECK_OPEN but returns -1 instead of NULL */ +#define P4EST_FILE_CHECK_INT(errcode, user_msg, cperrcode) do {\ + P4EST_FILE_CHECK_VERBOSE (errcode, user_msg); \ + *cperrcode = errcode; \ + if (!P4EST_FILE_IS_SUCCESS (errcode)) { \ + p4est_file_error_code (errcode, cperrcode); \ + return -1;}} while (0) + +/** This macro prints the MPI error for sc_mpi_{read,write}_all and return NULL. + * This means that this macro is appropriate to call it after a collective + * read or write. + */ +#define P4EST_FILE_CHECK_NULL(errcode, fc, user_msg, cperrcode) do {\ + P4EST_FILE_CHECK_VERBOSE (errcode, user_msg);\ + *cperrcode = errcode; \ + if (!P4EST_FILE_IS_SUCCESS (errcode)) { \ + p4est_file_error_cleanup (&fc->file); \ + P4EST_FREE (fc); \ + p4est_file_error_code (errcode, cperrcode);\ + return NULL;}} while (0) + +/** This macro prints the MPI error for sc_mpi_{read,write}. + * This means that this macro is appropriate to call it after a non-collective + * read or write. For a correct error handling it is required to skip the rest + * of the non-collective code and then broadcast the error flag. + * Can be used only multiple times in a function but will always jump to the + * same label. This leads to correct error managing. + */ +#define P4EST_FILE_CHECK_MPI(errcode, user_msg) do {P4EST_FILE_CHECK_VERBOSE (errcode, user_msg);\ + if (!P4EST_FILE_IS_SUCCESS (mpiret)) {\ + goto p4est_read_write_error;}} while (0) + +/** Use this macro after \ref P4EST_FILE_CHECK_MPI *directly* after the end of + * non-collective statements. + * Can be only used once in a function. + */ +/* Remark: Since we use a declaration after the label we need an empty statement. */ +#define P4EST_HANDLE_MPI_ERROR(mpiret,fc,comm,cperrcode) do {p4est_read_write_error: ; \ + int p4est_mpiret_handle_error = \ + sc_MPI_Bcast (&mpiret, 1, sc_MPI_INT, 0, comm);\ + SC_CHECK_MPI (p4est_mpiret_handle_error); \ + *cperrcode = mpiret; \ + if (!P4EST_FILE_IS_SUCCESS (mpiret)) { \ + p4est_file_error_cleanup (&fc->file); \ + P4EST_FREE (fc); \ + p4est_file_error_code (mpiret, cperrcode); \ + return NULL;}} while (0) + +/** A macro to check for file write related count errors. + * These errors are handled as fatal errors. The macro is only applicable for + * collective calls. + */ +#define P4EST_FILE_CHECK_COUNT(icount,ocount,fc,cperrcode) do { int p4est_count_error_global, p4est_mpiret,\ + p4est_rank; \ + int p4est_file_check_count = ((int) icount != ocount); \ + p4est_mpiret = sc_MPI_Allreduce (&p4est_file_check_count, \ + &p4est_count_error_global, 1, sc_MPI_INT, sc_MPI_LOR, \ + fc->mpicomm); \ + SC_CHECK_MPI (p4est_mpiret); \ + p4est_mpiret = sc_MPI_Comm_rank (fc->mpicomm, &p4est_rank);\ + SC_CHECK_MPI (p4est_mpiret); \ + *cperrcode = (p4est_file_check_count) ? \ + P4EST_FILE_ERR_COUNT : sc_MPI_SUCCESS; \ + if (p4est_count_error_global) \ + { if (p4est_rank == 0) { \ + SC_LERRORF ("Count error at %s:%d.\n",__FILE__, \ + __LINE__);} \ + p4est_file_error_cleanup (&fc->file); \ + P4EST_FREE (fc); \ + return NULL;}} while (0) + +/** A macro to check for file write related count errors. This macro is + * only applicable for serial calls. The errors are handled as fatal errors. + * We assume that the macro is called on rank 0. + */ +#define P4EST_FILE_CHECK_COUNT_SERIAL(icount, ocount) do {if (((int) icount) != ocount) { \ + SC_LERRORF ("Count error on rank 0 at %s:%d.\n",__FILE__,\ + __LINE__); \ + goto p4est_write_count_error;}} while (0) + +/** A macro to handle a file write error that occurred on rank 0 but need to be + * handled collectivly. We need count_error as input since we need a variable to + * broadcast the count error status. count_error is true if there is a count error + * and false otherwise. + */ +/* Remark: Since we use a declaration after the label we need an empty statement. */ +#define P4EST_HANDLE_MPI_COUNT_ERROR(count_error,fc,cperrcode) do {p4est_write_count_error: ;\ + int p4est_mpiret_handle = sc_MPI_Bcast (&count_error, 1, sc_MPI_INT, 0,\ + fc->mpicomm);\ + SC_CHECK_MPI (p4est_mpiret_handle);\ + *cperrcode = (count_error) ? P4EST_FILE_ERR_COUNT : sc_MPI_SUCCESS;\ + if (count_error) {\ + p4est_file_error_cleanup (&fc->file);\ + P4EST_FREE (fc);\ + return NULL;}} while (0) + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ sc_array_t * p4est_deflate_quadrants (p4est_t * p4est, sc_array_t ** data) @@ -84,11 +219,13 @@ p4est_deflate_quadrants (p4est_t * p4est, sc_array_t ** data) return qarr; } -p4est_t * -p4est_inflate (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, - const p4est_gloidx_t * global_first_quadrant, - const p4est_gloidx_t * pertree, - sc_array_t * quadrants, sc_array_t * data, void *user_pointer) +static p4est_t * +p4est_inflate_internal (sc_MPI_Comm mpicomm, + p4est_connectivity_t * connectivity, + const p4est_gloidx_t * global_first_quadrant, + const p4est_gloidx_t * pertree, + sc_array_t * quadrants, sc_array_t * data, + void *user_pointer) { const p4est_gloidx_t *gfq; int i; @@ -164,7 +301,7 @@ p4est_inflate (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, else { p4est->user_data_pool = NULL; } - p4est->quadrant_pool = sc_mempool_new (sizeof (p4est_quadrant_t)); + p4est->quadrant_pool = p4est_quadrant_mempool_new (); /* find the first and last tree on this processor */ if (p4est->local_num_quadrants > 0) { @@ -257,9 +394,1916 @@ p4est_inflate (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, (long long) p4est->local_num_quadrants); P4EST_ASSERT (p4est->revision == 0); - P4EST_ASSERT (p4est_is_valid (p4est)); p4est_log_indent_pop (); P4EST_GLOBAL_PRODUCTION ("Done " P4EST_STRING "_inflate\n"); return p4est; + +} + +p4est_t * +p4est_inflate (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, + const p4est_gloidx_t * global_first_quadrant, + const p4est_gloidx_t * pertree, + sc_array_t * quadrants, sc_array_t * data, void *user_pointer) +{ + p4est_t *ret_p4est; + + ret_p4est = p4est_inflate_internal (mpicomm, connectivity, + global_first_quadrant, + pertree, quadrants, data, user_pointer); + P4EST_ASSERT (p4est_is_valid (ret_p4est)); + + return ret_p4est; +} + +p4est_t * +p4est_inflate_null (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, + const p4est_gloidx_t * global_first_quadrant, + const p4est_gloidx_t * pertree, + sc_array_t * quadrants, sc_array_t * data, + void *user_pointer) +{ + p4est_t *ret_p4est; + + ret_p4est = p4est_inflate_internal (mpicomm, connectivity, + global_first_quadrant, + pertree, quadrants, data, user_pointer); + + if (!p4est_is_valid (ret_p4est)) { + p4est_destroy (ret_p4est); + return NULL; + } + + return ret_p4est; +} + +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +/* Avoid redefinition in p4est_to_p8est.h */ +#ifdef P4_TO_P8 +#define p4est_file_context p8est_file_context +#endif + +/** The opaque file context for for p4est data files. */ +struct p4est_file_context +{ + sc_MPI_Comm mpicomm; /**< corresponding MPI communicator */ + p4est_locidx_t local_num_quadrants; /**< number of local quadrants */ + p4est_gloidx_t global_num_quadrants; /**< number of global quadrants */ + p4est_gloidx_t *global_first_quadrant; /**< represents the partition */ + int gfq_owned; /**< Boolean to indicate if global_first_quadrant + is owned. */ + size_t num_calls; /**< redundant but for convenience; + counts the number of calls of + write and read, respectively */ + sc_MPI_File file; /**< file object */ + sc_MPI_Offset accessed_bytes; /**< count only array data bytes and + array metadata bytes */ +}; + +/** This function calculates a padding string consisting of spaces. + * We require an already allocated array pad or NULL. + * The number of bytes in pad must be at least divisor + 1! + * For NULL the function calculates only the number of padding bytes. + */ +static void +p4est_file_get_padding_string (size_t num_bytes, size_t divisor, char *pad, + size_t *num_pad_bytes) +{ + P4EST_ASSERT (divisor != 0 && num_pad_bytes != NULL); + + *num_pad_bytes = (divisor - (num_bytes % divisor)) % divisor; + if (*num_pad_bytes == 0 || *num_pad_bytes == 1) { + /* In these cases there is no space to add new line characters + * but this is necessary to ensure a consistent layout in a text editor + */ + *num_pad_bytes += divisor; + } + + P4EST_ASSERT (*num_pad_bytes > 1); + P4EST_ASSERT (*num_pad_bytes <= divisor); + if (pad != NULL) { + snprintf (pad, *num_pad_bytes + 1, "\n%-*s\n", (int) *num_pad_bytes - 2, + ""); + } +} + +static int +p4est_file_check_file_metadata (sc_MPI_Comm mpicomm, const char *filename, + char + user_string[P4EST_FILE_USER_STRING_BYTES], + char *metadata, + p4est_gloidx_t * global_num_quadrants) +{ + long read_global_num_quads; + int mpiret, rank; + int error_flag; + + P4EST_ASSERT (metadata != NULL); + + error_flag = 0; + + mpiret = sc_MPI_Comm_rank (mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + /* check magic number */ + if (metadata[P4EST_FILE_MAGIC_BYTES - 1] != '\n') { + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Wrong file header format.\n"); + } + return P4EST_FILE_ERR_FORMAT; + } + + metadata[P4EST_FILE_MAGIC_BYTES - 1] = '\0'; + if (strcmp (metadata, P4EST_FILE_MAGIC_NUMBER)) { + /* TODO: check for wrong endianness */ + P4EST_LERRORF (P4EST_STRING + "_io: Error reading <%s>. Wrong magic number (in file = %s, magic number = %s).\n", + filename, metadata, P4EST_FILE_MAGIC_NUMBER); + error_flag = 1; + } + + /* check format of version string line */ + if (metadata[P4EST_FILE_MAGIC_BYTES + P4EST_FILE_VERSION_STR_BYTES - 1] != + '\n') { + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Wrong file header format.\n"); + } + return P4EST_FILE_ERR_FORMAT; + } + + metadata[P4EST_FILE_MAGIC_BYTES + P4EST_FILE_VERSION_STR_BYTES - 1] = '\0'; + if (strlen (&metadata[P4EST_FILE_MAGIC_BYTES]) != + P4EST_FILE_VERSION_STR_BYTES - 1) { + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Wrong file header format.\n"); + } + return P4EST_FILE_ERR_FORMAT; + } + + /* check the format of the user string */ + if (metadata + [P4EST_FILE_MAGIC_BYTES + P4EST_FILE_VERSION_STR_BYTES + + P4EST_FILE_USER_STRING_BYTES - 1] != '\n') { + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Wrong file header format.\n"); + } + return P4EST_FILE_ERR_FORMAT; + } + /* the content of the user string is not checked */ + sc_strcopy (user_string, P4EST_FILE_USER_STRING_BYTES - 1, + &metadata[P4EST_FILE_MAGIC_BYTES + + P4EST_FILE_VERSION_STR_BYTES]); + user_string[P4EST_FILE_USER_STRING_BYTES - 1] = '\0'; + + /* check number of global quadrants */ + /* there is no \n at the end of this line */ + metadata[P4EST_FILE_METADATA_BYTES] = '\0'; + + if (strlen + (&metadata + [P4EST_FILE_MAGIC_BYTES + P4EST_FILE_VERSION_STR_BYTES + + P4EST_FILE_USER_STRING_BYTES]) != 16) { + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Wrong file header format.\n"); + } + return P4EST_FILE_ERR_FORMAT; + } + + read_global_num_quads = + sc_atol (&metadata + [P4EST_FILE_MAGIC_BYTES + P4EST_FILE_VERSION_STR_BYTES + + P4EST_FILE_USER_STRING_BYTES]); + *global_num_quadrants = (p4est_gloidx_t) read_global_num_quads; + if (read_global_num_quads < 0) { + P4EST_LERRORF (P4EST_STRING + "_io: Error reading <%s>. Negative global number of quadrants.\n", + filename); + error_flag = 1; + } + + return (error_flag) ? P4EST_FILE_ERR_FORMAT : sc_MPI_SUCCESS; +} + +/** Close an MPI file or its libsc-internal replacement in case of an error. + * \param [in,out] file A sc_MPI_file + * \return Always -1 since this function is only called + * if an error already occurred. + */ +static int +p4est_file_error_cleanup (sc_MPI_File * file) +{ + /* no error checking since we are called under an error condition */ + P4EST_ASSERT (file != NULL); + if (*file != sc_MPI_FILE_NULL) { + /* We do not use here the libsc closing function since we do not perform + * error checking in this function that is only called if we had already + * an error. + */ +#ifdef P4EST_ENABLE_MPIIO + MPI_File_close (file); +#else + { +#ifdef P4EST_ENABLE_MPI + int rank, mpiret; +#endif + +#ifdef P4EST_ENABLE_MPI + mpiret = sc_MPI_Comm_rank ((*file)->mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + if (rank == 0) { +#endif + fclose ((*file)->file); + (*file)->file = NULL; +#ifdef P4EST_ENABLE_MPI + } +#endif + } +#endif + } +#ifndef P4EST_ENABLE_MPIIO + SC_FREE (*file); +#endif + return -1; +} + +static int p4est_file_error_code (int errcode, int *p4est_errcode); + +p4est_file_context_t * +p4est_file_open_create (p4est_t * p4est, const char *filename, + const char *user_string, int *errcode) +{ + int mpiret, count, count_error, mpisize; + /* We enforce the padding of the file header. */ + char metadata[P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV + 1]; + p4est_file_context_t *file_context; + + P4EST_ASSERT (p4est_is_valid (p4est)); + P4EST_ASSERT (filename != NULL); + P4EST_ASSERT (errcode != NULL); + + if (!(strlen (user_string) < P4EST_FILE_USER_STRING_BYTES)) { + /* invalid user string */ + *errcode = P4EST_FILE_ERR_IN_DATA; + /* We do not use p4est file error macro since there is no + * file context to clean up. + */ + P4EST_FILE_CHECK_VERBOSE (*errcode, + P4EST_STRING + "_open_create: Invalid user string"); + return NULL; + } + + if (!(p4est->global_num_quadrants <= P4EST_FILE_MAX_GLOBAL_QUAD)) { + /* number of global quadrant can not be written to the file header */ + *errcode = P4EST_FILE_ERR_IN_DATA; + /* We do not use p4est file error macro since there is no + * file context to clean up. + */ + P4EST_FILE_CHECK_VERBOSE (*errcode, + P4EST_STRING + "_open_create: Invalid number of global quadrants"); + return NULL; + } + + file_context = P4EST_ALLOC (p4est_file_context_t, 1); + + /* Open the file and create a new file if necessary */ + mpiret = + sc_io_open (p4est->mpicomm, filename, + SC_IO_WRITE_CREATE, sc_MPI_INFO_NULL, &file_context->file); + P4EST_FILE_CHECK_OPEN (mpiret, file_context, "File open create", errcode); + + if (p4est->mpirank == 0) { + /* write padded p4est-defined header */ + snprintf (metadata, P4EST_FILE_METADATA_BYTES + P4EST_FILE_BYTE_DIV + 1, + "%.7s\n%-23s\n%-47s\n%.16lld\n%-14s\n", P4EST_FILE_MAGIC_NUMBER, + p4est_version (), user_string, + (long long) p4est->global_num_quadrants, ""); + mpiret = + sc_io_write_at (file_context->file, 0, metadata, + P4EST_FILE_METADATA_BYTES + P4EST_FILE_BYTE_DIV, + sc_MPI_BYTE, &count); + P4EST_FILE_CHECK_MPI (mpiret, "Writing the file header"); + count_error = (P4EST_FILE_METADATA_BYTES + P4EST_FILE_BYTE_DIV != count); + P4EST_FILE_CHECK_COUNT_SERIAL (P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV, count); + } + + P4EST_HANDLE_MPI_ERROR (mpiret, file_context, p4est->mpicomm, errcode); + + /* initialize the file context */ + file_context->mpicomm = p4est->mpicomm; + file_context->local_num_quadrants = p4est->local_num_quadrants; + file_context->global_num_quadrants = p4est->global_num_quadrants; + mpiret = sc_MPI_Comm_size (p4est->mpicomm, &mpisize); + file_context->global_first_quadrant = + P4EST_ALLOC (p4est_gloidx_t, mpisize + 1); + memcpy (file_context->global_first_quadrant, p4est->global_first_quadrant, + (mpisize + 1) * sizeof (p4est_gloidx_t)); + file_context->gfq_owned = 1; + + P4EST_HANDLE_MPI_COUNT_ERROR (count_error, file_context, errcode); + + file_context->accessed_bytes = 0; + file_context->num_calls = 0; + + p4est_file_error_code (*errcode, errcode); + return file_context; +} + +p4est_file_context_t * +p4est_file_open_read_ext (sc_MPI_Comm mpicomm, const char *filename, + char *user_string, + p4est_gloidx_t * global_num_quadrants, int *errcode) +{ + int mpiret, rank; + int count, count_error; + char metadata[P4EST_FILE_METADATA_BYTES + 1]; + p4est_file_context_t *file_context = P4EST_ALLOC (p4est_file_context_t, 1); + + P4EST_ASSERT (filename != NULL); + P4EST_ASSERT (user_string != NULL); + P4EST_ASSERT (global_num_quadrants != NULL); + P4EST_ASSERT (errcode != NULL); + + /* Open the file in the reading mode */ + mpiret = + sc_io_open (mpicomm, filename, SC_IO_READ, + sc_MPI_INFO_NULL, &file_context->file); + P4EST_FILE_CHECK_OPEN (mpiret, file_context, "File open read", errcode); + + file_context->mpicomm = mpicomm; + file_context->local_num_quadrants = 0; /* not set for read calls */ + file_context->global_first_quadrant = NULL; + file_context->gfq_owned = 0; + file_context->accessed_bytes = 0; + file_context->num_calls = 0; + + /* get the MPI rank */ + mpiret = sc_MPI_Comm_rank (mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + /* read metadata and deallocate in case of error */ + if (rank == 0) { + /* read metadata on rank 0 */ + mpiret = + sc_io_read_at (file_context->file, 0, metadata, + P4EST_FILE_METADATA_BYTES, sc_MPI_BYTE, &count); + + P4EST_FILE_CHECK_MPI (mpiret, "Reading metadata"); + count_error = (P4EST_FILE_METADATA_BYTES != count); + P4EST_FILE_CHECK_COUNT_SERIAL (P4EST_FILE_METADATA_BYTES, count); + + metadata[P4EST_FILE_METADATA_BYTES] = '\0'; + /* parse metadata; we do not use file_info because we do not want a Bcast */ + mpiret = + p4est_file_check_file_metadata (mpicomm, filename, user_string, + metadata, global_num_quadrants); + P4EST_FILE_CHECK_MPI (mpiret, "Check file header"); + } + + /* error checking */ + P4EST_HANDLE_MPI_ERROR (mpiret, file_context, mpicomm, errcode); + P4EST_HANDLE_MPI_COUNT_ERROR (count_error, file_context, errcode); + + /* broadcast the user string of the file */ + mpiret = + sc_MPI_Bcast (user_string, P4EST_FILE_USER_STRING_BYTES, sc_MPI_BYTE, 0, + mpicomm); + SC_CHECK_MPI (mpiret); + + /* broadcast the number of global quadrants */ + mpiret = + sc_MPI_Bcast (global_num_quadrants, sizeof (p4est_gloidx_t), sc_MPI_BYTE, + 0, mpicomm); + SC_CHECK_MPI (mpiret); + + file_context->global_num_quadrants = *global_num_quadrants; + + p4est_file_error_code (*errcode, errcode); + return file_context; +} + +p4est_file_context_t * +p4est_file_open_read (p4est_t * p4est, const char *filename, + char *user_string, int *errcode) +{ + p4est_gloidx_t global_num_quadrants; + p4est_file_context_t *fc; + + P4EST_ASSERT (p4est_is_valid (p4est)); + P4EST_ASSERT (filename != NULL); + P4EST_ASSERT (user_string != NULL); + P4EST_ASSERT (errcode != NULL); + + fc = + p4est_file_open_read_ext (p4est->mpicomm, filename, user_string, + &global_num_quadrants, errcode); + + /* check global number of quadrants */ + if (fc != NULL && p4est->global_num_quadrants != global_num_quadrants) { + if (p4est->mpirank == 0) { + P4EST_LERRORF (P4EST_STRING "_file_open_read: global number of " + "quadrants mismatch (in file = %lld," + " by parameter = %lld)\n", + (long long) global_num_quadrants, + (long long) p4est->global_num_quadrants); + } + p4est_file_close (fc, errcode); + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING "_file_open_read: close file", + errcode); + p4est_file_error_code (*errcode, errcode); + return NULL; + } + + if (fc != NULL) { + /* use the partition of the given p4est */ + fc->global_first_quadrant = p4est->global_first_quadrant; + fc->gfq_owned = 0; + } + + p4est_file_error_code (*errcode, errcode); + return fc; +} + +p4est_file_context_t * +p4est_file_write_block (p4est_file_context_t * fc, size_t block_size, + sc_array_t * block_data, + const char *user_string, int *errcode) +{ + size_t num_pad_bytes; + char header_metadata[P4EST_FILE_FIELD_HEADER_BYTES + 1], + pad[P4EST_FILE_MAX_NUM_PAD_BYTES]; + int mpiret, count, count_error, rank; + + P4EST_ASSERT (fc != NULL); + P4EST_ASSERT (fc->global_first_quadrant != NULL); + P4EST_ASSERT (block_data != NULL); + P4EST_ASSERT (block_size == 0 || block_data->array != NULL); + P4EST_ASSERT (block_size == block_data->elem_size); + P4EST_ASSERT (block_data->elem_count == 1); + P4EST_ASSERT (errcode != NULL); + + if (!(strlen (user_string) < P4EST_FILE_USER_STRING_BYTES)) { + /* invalid user string */ + *errcode = P4EST_FILE_ERR_IN_DATA; + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING + "_file_write_block: Invalid user string", errcode); + } + + if (!(block_size <= P4EST_FILE_MAX_BLOCK_SIZE)) { + /* invalid header size */ + *errcode = P4EST_FILE_ERR_IN_DATA; + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING + "_file_write_block: Invalid block size", errcode); + } + + mpiret = sc_MPI_Comm_rank (fc->mpicomm, &rank); + SC_CHECK_MPI (mpiret); + +#ifdef P4EST_ENABLE_MPIIO + /* set the file size */ + mpiret = MPI_File_set_size (fc->file, + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV + block_size + + P4EST_FILE_FIELD_HEADER_BYTES + + fc->accessed_bytes); + P4EST_FILE_CHECK_NULL (mpiret, fc, "Set file size", errcode); +#else + /* We do not perform this optimization without MPI I/O */ +#endif + + num_pad_bytes = 0; + if (rank == 0) { + /* header-dependent metadata */ + snprintf (header_metadata, + P4EST_FILE_FIELD_HEADER_BYTES + + 1, "B %.13llu\n%-47s\n", (unsigned long long) block_size, + user_string); + + /* write header-dependent metadata */ + mpiret = + sc_io_write_at (fc->file, + fc->accessed_bytes + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV, header_metadata, + P4EST_FILE_FIELD_HEADER_BYTES, sc_MPI_BYTE, &count); + + P4EST_FILE_CHECK_MPI (mpiret, "Writing header metadata"); + count_error = (P4EST_FILE_FIELD_HEADER_BYTES != count); + P4EST_FILE_CHECK_COUNT_SERIAL (P4EST_FILE_FIELD_HEADER_BYTES, count); + } + P4EST_HANDLE_MPI_ERROR (mpiret, fc, fc->mpicomm, errcode); + P4EST_HANDLE_MPI_COUNT_ERROR (count_error, fc, errcode); + + /*write header data */ + if (rank == 0) { + mpiret = + sc_io_write_at (fc->file, + fc->accessed_bytes + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV + P4EST_FILE_FIELD_HEADER_BYTES, + block_data->array, block_size, sc_MPI_BYTE, &count); + + P4EST_FILE_CHECK_MPI (mpiret, "Writing block data"); + count_error = ((int) block_size != count); + P4EST_FILE_CHECK_COUNT_SERIAL (block_size, count); + + /* write padding bytes */ + p4est_file_get_padding_string (block_size, P4EST_FILE_BYTE_DIV, pad, + &num_pad_bytes); + mpiret = + sc_io_write_at (fc->file, + fc->accessed_bytes + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV + P4EST_FILE_FIELD_HEADER_BYTES + + block_size, pad, num_pad_bytes, sc_MPI_BYTE, &count); + P4EST_FILE_CHECK_MPI (mpiret, "Writing padding bytes for header data"); + count_error = ((int) num_pad_bytes != count); + P4EST_FILE_CHECK_COUNT_SERIAL (num_pad_bytes, count); + } + else { + p4est_file_get_padding_string (block_size, P4EST_FILE_BYTE_DIV, NULL, + &num_pad_bytes); + } + + /* This is *not* the processor local value */ + fc->accessed_bytes += + block_size + P4EST_FILE_FIELD_HEADER_BYTES + num_pad_bytes; + ++fc->num_calls; + + p4est_file_error_code (*errcode, errcode); + return fc; +} + +/** Collectivly read and check block metadata. + * If user_string == NULL data_size is not compared to + * read_data_size. + */ +static p4est_file_context_t * +p4est_file_read_block_metadata (p4est_file_context_t * fc, + size_t *read_data_size, size_t data_size, + char block_type, + char *user_string, int *errcode) +{ + int mpiret, count, count_error, rank; + int bytes_to_read; + int err_flag, invalid_block; + char block_metadata[P4EST_FILE_FIELD_HEADER_BYTES]; + size_t data_block_size, num_pad_bytes; + + P4EST_ASSERT (read_data_size != NULL); + P4EST_ASSERT (errcode != NULL); + + mpiret = sc_MPI_Comm_rank (fc->mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + bytes_to_read = + (user_string != + NULL) ? P4EST_FILE_FIELD_HEADER_BYTES : (P4EST_FILE_ARRAY_METADATA_BYTES + + 2); + if (rank == 0) { + mpiret = sc_io_read_at (fc->file, + fc->accessed_bytes + + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV, block_metadata, + bytes_to_read, sc_MPI_BYTE, &count); + P4EST_FILE_CHECK_MPI (mpiret, "Reading data section-wise metadata"); + count_error = (bytes_to_read != count); + P4EST_FILE_CHECK_COUNT_SERIAL (bytes_to_read, count); + } + /* In the case of error the return value is still NULL */ + P4EST_HANDLE_MPI_ERROR (mpiret, fc, fc->mpicomm, errcode); + P4EST_HANDLE_MPI_COUNT_ERROR (count_error, fc, errcode); + + /* broadcast block metadata to calculate correct internals on each rank */ + mpiret = + sc_MPI_Bcast (block_metadata, bytes_to_read, sc_MPI_BYTE, 0, fc->mpicomm); + SC_CHECK_MPI (mpiret); + + /* check for given block specifying character */ + invalid_block = 0; + if (block_metadata[0] != block_type) { + invalid_block = block_metadata[0] != 'F' && block_metadata[0] != 'B'; + if (rank == 0) { + if (invalid_block) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Invalid data section type.\n"); + } + else { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Wrong data section type.\n"); + } + } + p4est_file_error_cleanup (&fc->file); + P4EST_FREE (fc); + if (invalid_block) { + *errcode = P4EST_FILE_ERR_FORMAT; + } + else { + *errcode = P4EST_FILE_ERR_SECTION_TYPE; + } + return NULL; + } + + /* check '\n' to check the format */ + if (block_metadata[P4EST_FILE_ARRAY_METADATA_BYTES + 1] != '\n') { + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Wrong section header format.\n"); + } + p4est_file_error_cleanup (&fc->file); + P4EST_FREE (fc); + *errcode = P4EST_FILE_ERR_FORMAT; + return NULL; + } + + /* process the block metadata */ + block_metadata[P4EST_FILE_ARRAY_METADATA_BYTES + 1] = '\0'; + /* we cut off the block type specifier */ + *read_data_size = sc_atol (&block_metadata[2]); + + if (user_string != NULL && *read_data_size != data_size) { + if (rank == 0) { + P4EST_LERRORF (P4EST_STRING + "_io: Error reading. Wrong section data size (in file = %ld, by parameter = %ld).\n", + *read_data_size, data_size); + } + p4est_file_error_cleanup (&fc->file); + P4EST_FREE (fc); + *errcode = P4EST_FILE_ERR_FORMAT; + return NULL; + } + + if (user_string != NULL) { + /* check '\n' to check the format */ + if (block_metadata[P4EST_FILE_FIELD_HEADER_BYTES - 1] != '\n') { + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Wrong section header format.\n"); + } + p4est_file_error_cleanup (&fc->file); + P4EST_FREE (fc); + *errcode = P4EST_FILE_ERR_FORMAT; + return NULL; + } + + /* null-terminate the user string of the current block */ + block_metadata[P4EST_FILE_FIELD_HEADER_BYTES - 1] = '\0'; + + /* copy the user string, '\0' was already set above */ + sc_strcopy (user_string, P4EST_FILE_USER_STRING_BYTES, + &block_metadata[P4EST_FILE_ARRAY_METADATA_BYTES + 2]); + P4EST_ASSERT (user_string[P4EST_FILE_USER_STRING_BYTES - 1] == '\0'); + } + + /* check the padding structure */ + err_flag = 0; + if (rank == 0) { + /* calculate number of padding bytes */ + if (block_metadata[0] == 'F') { + data_block_size = *read_data_size * fc->global_num_quadrants; + } + else if (block_metadata[0] == 'B') { + data_block_size = *read_data_size; + } + else { + /* We assume that this function is called for a valid block type. */ + SC_ABORT_NOT_REACHED (); + } + p4est_file_get_padding_string (data_block_size, P4EST_FILE_BYTE_DIV, NULL, + &num_pad_bytes); + /* read padding bytes */ + mpiret = sc_io_read_at (fc->file, + fc->accessed_bytes + + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV + + P4EST_FILE_FIELD_HEADER_BYTES + data_block_size, + block_metadata, num_pad_bytes, sc_MPI_BYTE, + &count); + P4EST_FILE_CHECK_MPI (mpiret, "Reading padding bytes"); + count_error = ((int) num_pad_bytes != count); + P4EST_FILE_CHECK_COUNT_SERIAL (num_pad_bytes, count); + /* check '\n' in padding bytes */ + if (block_metadata[0] != '\n' + || block_metadata[num_pad_bytes - 1] != '\n') { + err_flag = 1; + } + } + /* broadcast error status */ + mpiret = sc_MPI_Bcast (&err_flag, 1, sc_MPI_INT, 0, fc->mpicomm); + SC_CHECK_MPI (mpiret); + + if (err_flag) { + /* wrong padding format */ + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. Wrong padding format.\n"); + } + p4est_file_error_cleanup (&fc->file); + P4EST_FREE (fc); + *errcode = P4EST_FILE_ERR_FORMAT; + return NULL; + } + + return fc; +} + +p4est_file_context_t * +p4est_file_read_block (p4est_file_context_t * fc, + size_t block_size, sc_array_t * block_data, + char *user_string, int *errcode) +{ + int mpiret, count, count_error, rank; + size_t num_pad_bytes, read_data_size; +#ifdef P4EST_ENABLE_MPIIO + sc_MPI_Offset size; +#endif + + P4EST_ASSERT (fc != NULL); + P4EST_ASSERT (block_data == NULL || block_size == block_data->elem_size); + P4EST_ASSERT (block_data == NULL || block_data->elem_count == 1); + P4EST_ASSERT (errcode != NULL); + P4EST_ASSERT (user_string != NULL); + + mpiret = sc_MPI_Comm_rank (fc->mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + num_pad_bytes = 0; + /* calculate the padding bytes for this header data */ + p4est_file_get_padding_string (block_size, P4EST_FILE_BYTE_DIV, NULL, + &num_pad_bytes); + if (block_data == NULL) { + /* Nothing to read but we shift our own file pointer */ + if (p4est_file_read_block_metadata + (fc, &read_data_size, block_size, 'B', user_string, + errcode) == NULL) { + p4est_file_error_code (*errcode, errcode); + return NULL; + } + + fc->accessed_bytes += + block_size + P4EST_FILE_FIELD_HEADER_BYTES + num_pad_bytes; + ++fc->num_calls; + *errcode = sc_MPI_SUCCESS; + p4est_file_error_code (*errcode, errcode); + return fc; + } + + P4EST_ASSERT (block_size == block_data->elem_size); + +#ifdef P4EST_ENABLE_MPIIO + /* check file size; no sync required because the file size does not + * change in the reading mode. + */ + mpiret = MPI_File_get_size (fc->file, &size); + P4EST_FILE_CHECK_NULL (mpiret, fc, "Get file size for read", errcode); + if (((size_t) size) - P4EST_FILE_METADATA_BYTES - P4EST_FILE_BYTE_DIV - + P4EST_FILE_FIELD_HEADER_BYTES < block_size) { + /* report wrong file size, collectively close the file and deallocate fc */ + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. File has less bytes than the user wants to read.\n"); + } + mpiret = p4est_file_close (fc, &mpiret); + P4EST_FILE_CHECK_NULL (mpiret, fc, + P4EST_STRING "_file_read_data: close file", + errcode); + p4est_file_error_code (*errcode, errcode); + return NULL; + } +#else + /* There is no C-standard functionality to get the file size */ +#endif + + /* check the header metadata */ + if (p4est_file_read_block_metadata + (fc, &read_data_size, block_size, 'B', user_string, errcode) == NULL) { + p4est_file_error_code (*errcode, errcode); + return NULL; + } + + if (rank == 0) { + mpiret = sc_io_read_at (fc->file, fc->accessed_bytes + + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_FIELD_HEADER_BYTES + + P4EST_FILE_BYTE_DIV, block_data->array, + (int) block_size, sc_MPI_BYTE, &count); + P4EST_FILE_CHECK_MPI (mpiret, "Reading header data"); + count_error = ((int) block_size != count); + P4EST_FILE_CHECK_COUNT_SERIAL (block_size, count); + } + P4EST_HANDLE_MPI_ERROR (mpiret, fc, fc->mpicomm, errcode); + P4EST_HANDLE_MPI_COUNT_ERROR (count_error, fc, errcode); + + /* broadcast the header data */ + mpiret = + sc_MPI_Bcast (block_data->array, block_size, sc_MPI_BYTE, 0, fc->mpicomm); + SC_CHECK_MPI (mpiret); + + fc->accessed_bytes += + block_size + P4EST_FILE_FIELD_HEADER_BYTES + num_pad_bytes; + ++fc->num_calls; + + p4est_file_error_code (*errcode, errcode); + return fc; } + +p4est_file_context_t * +p4est_file_write_field (p4est_file_context_t * fc, size_t quadrant_size, + sc_array_t * quadrant_data, const char *user_string, + int *errcode) +{ + size_t bytes_to_write, num_pad_bytes, array_size; + char array_metadata[P4EST_FILE_FIELD_HEADER_BYTES + 1], + pad[P4EST_FILE_MAX_NUM_PAD_BYTES]; + sc_MPI_Offset write_offset; + int mpiret, count, count_error, rank; + + P4EST_ASSERT (fc != NULL); + P4EST_ASSERT (quadrant_data != NULL + && (quadrant_data->elem_count == 0 + || quadrant_data->elem_count == + (size_t) fc->local_num_quadrants)); + P4EST_ASSERT (quadrant_size == quadrant_data->elem_size); + P4EST_ASSERT (errcode != NULL); + + if (!(strlen (user_string) < P4EST_FILE_USER_STRING_BYTES)) { + *errcode = P4EST_FILE_ERR_IN_DATA; + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING + "_file_write_field: Invalid user string", errcode); + } + + if (!(quadrant_data->elem_size <= P4EST_FILE_MAX_FIELD_ENTRY_SIZE)) { + *errcode = P4EST_FILE_ERR_IN_DATA; + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING + "_file_write_field: Invalid byte number per field entry", + errcode); + } + + mpiret = sc_MPI_Comm_rank (fc->mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + /* Check how many bytes we write to the disk */ + bytes_to_write = quadrant_data->elem_count * quadrant_data->elem_size; + + /* rank-dependent byte offset */ + write_offset = P4EST_FILE_METADATA_BYTES + P4EST_FILE_BYTE_DIV + + fc->global_first_quadrant[rank] * quadrant_data->elem_size; + +#ifdef P4EST_ENABLE_MPIIO + /* set the file size */ + mpiret = MPI_File_set_size (fc->file, + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV + + fc->global_num_quadrants * + quadrant_data->elem_size + + P4EST_FILE_FIELD_HEADER_BYTES + + fc->accessed_bytes); + P4EST_FILE_CHECK_NULL (mpiret, fc, "Set file size", errcode); +#else + /* We do not perform this optimization without MPI I/O */ +#endif + + num_pad_bytes = 0; + if (rank == 0) { + /* array-dependent metadata */ + snprintf (array_metadata, + P4EST_FILE_FIELD_HEADER_BYTES + + 1, "F %.13llu\n%-47s\n", + (unsigned long long) quadrant_data->elem_size, user_string); + + /* write array-dependent metadata */ + mpiret = + sc_io_write_at (fc->file, fc->accessed_bytes + write_offset, + array_metadata, + P4EST_FILE_FIELD_HEADER_BYTES, sc_MPI_BYTE, &count); + + P4EST_FILE_CHECK_MPI (mpiret, "Writing array metadata"); + count_error = (P4EST_FILE_FIELD_HEADER_BYTES != count); + P4EST_FILE_CHECK_COUNT_SERIAL (P4EST_FILE_FIELD_HEADER_BYTES, count); + } + P4EST_HANDLE_MPI_ERROR (mpiret, fc, fc->mpicomm, errcode); + P4EST_HANDLE_MPI_COUNT_ERROR (count_error, fc, errcode); + + /* write array data */ + mpiret = + sc_io_write_at_all (fc->file, + fc->accessed_bytes + write_offset + + P4EST_FILE_FIELD_HEADER_BYTES, quadrant_data->array, + bytes_to_write, sc_MPI_BYTE, &count); + P4EST_FILE_CHECK_NULL (mpiret, fc, "Writing quadrant-wise", errcode); + P4EST_FILE_CHECK_COUNT (bytes_to_write, count, fc, errcode); + + /** We place the padding bytes write here because for the sequential + * IO operations the order of fwrite calls plays a role. + */ + /* write padding bytes */ + if (rank == 0) { + /* Calculate and write padding bytes for array data */ + array_size = fc->global_num_quadrants * quadrant_data->elem_size; + p4est_file_get_padding_string (array_size, P4EST_FILE_BYTE_DIV, pad, + &num_pad_bytes); + + mpiret = + sc_io_write_at (fc->file, + fc->accessed_bytes + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_BYTE_DIV + array_size + + P4EST_FILE_FIELD_HEADER_BYTES, pad, num_pad_bytes, + sc_MPI_BYTE, &count); + P4EST_FILE_CHECK_MPI (mpiret, "Writing padding bytes for a data array"); + /* We do not need to call P4EST_FILE_HANDLE_MPI_ERROR in the next + * collective line of code since P4EST_FILE_HANDLE_MPI_ERROR was already + * called in this scope. + */ + count_error = ((int) num_pad_bytes != count); + P4EST_FILE_CHECK_COUNT_SERIAL (num_pad_bytes, count); + } + else { + array_size = fc->global_num_quadrants * quadrant_data->elem_size; + p4est_file_get_padding_string (array_size, P4EST_FILE_BYTE_DIV, NULL, + &num_pad_bytes); + } + + /* This is *not* the processor local value */ + fc->accessed_bytes += + quadrant_data->elem_size * fc->global_num_quadrants + + P4EST_FILE_FIELD_HEADER_BYTES + num_pad_bytes; + ++fc->num_calls; + + p4est_file_error_code (*errcode, errcode); + return fc; +} + +p4est_file_context_t * +p4est_file_read_field_ext (p4est_file_context_t * fc, p4est_gloidx_t * gfq, + size_t quadrant_size, sc_array_t * quadrant_data, + char *user_string, int *errcode) +{ + int count; + size_t bytes_to_read, num_pad_bytes, array_size, + read_data_size; +#ifdef P4EST_ENABLE_MPIIO + sc_MPI_Offset size; +#endif + int mpiret, rank, mpisize; + + P4EST_ASSERT (fc != NULL); + + mpiret = sc_MPI_Comm_rank (fc->mpicomm, &rank); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Comm_size (fc->mpicomm, &mpisize); + SC_CHECK_MPI (mpiret); + + P4EST_ASSERT (gfq != NULL); + P4EST_ASSERT (errcode != NULL); + P4EST_ASSERT (user_string != NULL); + P4EST_ASSERT (quadrant_data == NULL + || quadrant_size == quadrant_data->elem_size); + + /* check gfq in the debug mode */ + P4EST_ASSERT (gfq[0] == 0); + P4EST_ASSERT (gfq[mpisize] == fc->global_num_quadrants); + + if (quadrant_data != NULL) { + sc_array_resize (quadrant_data, (size_t) (gfq[rank + 1] - gfq[rank])); + } + + /* check how many bytes we read from the disk */ + bytes_to_read = ((size_t) (gfq[rank + 1] - gfq[rank])) * quadrant_size; + +#ifdef P4EST_ENABLE_MPIIO + /* check file size; no sync required because the file size does not + * change in the reading mode. + */ + mpiret = MPI_File_get_size (fc->file, &size); + P4EST_FILE_CHECK_NULL (mpiret, fc, "Get file size for read", errcode); + if (((size_t) size) - P4EST_FILE_METADATA_BYTES - P4EST_FILE_BYTE_DIV - + P4EST_FILE_FIELD_HEADER_BYTES < bytes_to_read) { + /* report wrong file size, collectively close the file and deallocate fc */ + if (rank == 0) { + P4EST_LERROR (P4EST_STRING + "_io: Error reading. File has less bytes than the user wants to read.\n"); + } + mpiret = p4est_file_close (fc, &mpiret); + P4EST_FILE_CHECK_NULL (mpiret, fc, + P4EST_STRING "_file_read_data: close file", + errcode); + p4est_file_error_code (*errcode, errcode); + return NULL; + } +#else + /* There is no C-standard functionality to get the file size */ +#endif + + /* check the array metadata */ + if (p4est_file_read_block_metadata + (fc, &read_data_size, quadrant_size, 'F', user_string, + errcode) == NULL) { + p4est_file_error_code (*errcode, errcode); + return NULL; + } + + /* calculate the padding bytes for this data array */ + array_size = fc->global_num_quadrants * quadrant_size; + p4est_file_get_padding_string (array_size, P4EST_FILE_BYTE_DIV, NULL, + &num_pad_bytes); + + if (quadrant_data != NULL) { + mpiret = sc_io_read_at_all (fc->file, + fc->accessed_bytes + + P4EST_FILE_METADATA_BYTES + + P4EST_FILE_FIELD_HEADER_BYTES + + P4EST_FILE_BYTE_DIV + gfq[rank] + * quadrant_data->elem_size, + quadrant_data->array, bytes_to_read, + sc_MPI_BYTE, &count); + + P4EST_FILE_CHECK_NULL (mpiret, fc, "Reading quadrant-wise", errcode); + P4EST_FILE_CHECK_COUNT (bytes_to_read, count, fc, errcode); + } + + fc->accessed_bytes += + quadrant_size * fc->global_num_quadrants + + P4EST_FILE_FIELD_HEADER_BYTES + num_pad_bytes; + ++fc->num_calls; + + p4est_file_error_code (*errcode, errcode); + return fc; +} + +p4est_file_context_t * +p4est_file_read_field (p4est_file_context_t * fc, size_t quadrant_size, + sc_array_t * quadrant_data, char *user_string, + int *errcode) +{ + int mpiret, mpisize, rank; + p4est_gloidx_t *gfq = NULL; + p4est_file_context_t *retfc; + + P4EST_ASSERT (fc != NULL); + P4EST_ASSERT (errcode != NULL); + P4EST_ASSERT (quadrant_data == NULL + || quadrant_size == quadrant_data->elem_size); + + /* If this function is used on a file context obtained by + * \ref p4est_file_open_read the global_first_quadrant + * array is set to the corresponding one of the p4est + * given in the call \ref p4est_file_open_read. + * Otherwise the file context was obtained by calling + * \ref p4est_file_open_read_ext. This means the + * global_first_quadrant array is not set since + * there is no given p4est. In this case we + * compute a uniform partition. + */ + if (fc->global_first_quadrant == NULL) { + /* there is no partition set in the file context */ + mpiret = sc_MPI_Comm_size (fc->mpicomm, &mpisize); + SC_CHECK_MPI (mpiret); + + gfq = P4EST_ALLOC (p4est_gloidx_t, mpisize + 1); + + /* calculate gfq for a uniform partition */ + p4est_comm_global_first_quadrant (fc->global_num_quadrants, mpisize, gfq); + } + else { + gfq = fc->global_first_quadrant; + } + + mpiret = sc_MPI_Comm_rank (fc->mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + if (quadrant_data != NULL) { + /* allocate the memory for the quadrant data */ + sc_array_resize (quadrant_data, (size_t) (gfq[rank + 1] - gfq[rank])); + } + + retfc = p4est_file_read_field_ext (fc, gfq, quadrant_size, quadrant_data, + user_string, errcode); + if (fc->global_first_quadrant == NULL) { + P4EST_FREE (gfq); + } + + p4est_file_error_code (*errcode, errcode); + return retfc; +} + +/** This function checks for successful completion and cleans up if required. + * + * \param[in,out] file The MPI file that will be closed in case of an error. + * \param[in] eclass The eclass that indicates if an error occured. + * \b eclass is an MPI, libsc or p4est_file error + * code. + * \param[out] errcode The error code that is obtained by converting + * \b eclass to p4est_file error code. + * \return -1 if \b eclass indicates an error, + * 0 otherwise. + */ +static int +p4est_file_info_cleanup (sc_MPI_File * file, int eclass, int *errcode) +{ + if (!P4EST_FILE_IS_SUCCESS (eclass)) { + p4est_file_error_cleanup (file); + p4est_file_error_code (eclass, errcode); + return -1; + } + return 0; +} + +int +p4est_file_info (p4est_t * p4est, const char *filename, + char *user_string, sc_array_t * data_sections, int *errcode) +{ + int mpiret, eclass; + int count, count_error; + long long_header; + size_t current_size, num_pad_bytes; + char metadata[P4EST_FILE_METADATA_BYTES + 1]; + char block_metadata[P4EST_FILE_FIELD_HEADER_BYTES + 1]; + p4est_gloidx_t global_num_quadrants; + p4est_file_section_metadata_t *current_member; + sc_MPI_Offset current_position; + sc_MPI_File file; + + P4EST_ASSERT (p4est != NULL); + P4EST_ASSERT (p4est_is_valid (p4est)); + P4EST_ASSERT (filename != NULL); + P4EST_ASSERT (user_string != NULL); + P4EST_ASSERT (data_sections != NULL); + P4EST_ASSERT (data_sections->elem_size == + sizeof (p4est_file_section_metadata_t)); + P4EST_ASSERT (errcode != NULL); + + /* set default output values */ + sc_array_reset (data_sections); + + /* open the file in reading mode */ + *errcode = eclass = sc_MPI_SUCCESS; /* MPI defines MPI_SUCCESS to equal 0. */ + file = sc_MPI_FILE_NULL; + + /* we do not use the general error handling since we can not close the file */ + eclass = sc_io_open (p4est->mpicomm, filename, SC_IO_READ, + sc_MPI_INFO_NULL, &file); + + if (!P4EST_FILE_IS_SUCCESS (eclass)) { + *errcode = eclass; + SC_FREE (file); + p4est_file_error_code (*errcode, errcode); + return -1; + } + + /* read file metadata on root rank */ + P4EST_ASSERT (P4EST_FILE_IS_SUCCESS (eclass)); + if (p4est->mpirank == 0) { + if ((eclass = sc_io_read_at (file, 0, metadata, + P4EST_FILE_METADATA_BYTES, sc_MPI_BYTE, + &count)) + != sc_MPI_SUCCESS) { + *errcode = eclass; + /* There is no count error for a non-successful read. */ + count_error = 0; + } + else { + count_error = (P4EST_FILE_METADATA_BYTES != count); + } + } + mpiret = sc_MPI_Bcast (&eclass, 1, sc_MPI_INT, 0, p4est->mpicomm); + SC_CHECK_MPI (mpiret); + if (p4est_file_info_cleanup (&file, eclass, errcode)) { + /* an error has occured and a clean up was performed */ + return -1; + } + mpiret = sc_MPI_Bcast (&count_error, 1, sc_MPI_INT, 0, p4est->mpicomm); + SC_CHECK_MPI (mpiret); + if (count_error) { + if (p4est->mpirank == 0) { + P4EST_LERROR (P4EST_STRING + "_file_info: read count error for file metadata reading"); + } + eclass = P4EST_FILE_ERR_COUNT; + return p4est_file_info_cleanup (&file, eclass, errcode); + } + + /* broadcast file metadata to all ranks and null-terminate it */ + mpiret = sc_MPI_Bcast (metadata, P4EST_FILE_METADATA_BYTES, sc_MPI_BYTE, 0, + p4est->mpicomm); + SC_CHECK_MPI (mpiret); + metadata[P4EST_FILE_METADATA_BYTES] = '\0'; + + if ((mpiret = + p4est_file_check_file_metadata (p4est->mpicomm, filename, user_string, + metadata, + &global_num_quadrants)) != + sc_MPI_SUCCESS) { + eclass = P4EST_FILE_ERR_FORMAT; + return p4est_file_info_cleanup (&file, eclass, errcode); + } + + /* check global number of quadrants */ + if (p4est->global_num_quadrants != global_num_quadrants) { + if (p4est->mpirank == 0) { + P4EST_LERROR (P4EST_STRING + "_file_info: global number of quadrant mismatch"); + } + eclass = P4EST_FILE_ERR_FORMAT; + return p4est_file_info_cleanup (&file, eclass, errcode); + } + + current_position = + (sc_MPI_Offset) (P4EST_FILE_METADATA_BYTES + P4EST_FILE_BYTE_DIV); + + /* read all data headers that we find and skip the data itself */ + if (p4est->mpirank == 0) { + for (;;) { + /* read block metadata for current record */ + eclass = sc_io_read_at (file, current_position, block_metadata, + P4EST_FILE_FIELD_HEADER_BYTES, sc_MPI_BYTE, + &count); + if (p4est_file_info_cleanup (&file, eclass, errcode)) { + return -1; + } + if (P4EST_FILE_FIELD_HEADER_BYTES != count) { + /* we did not read the correct number of bytes */ + break; + } + + /* parse and store the element size, the block type and the user string */ + current_member = + (p4est_file_section_metadata_t *) sc_array_push (data_sections); + if (block_metadata[0] == 'B' || block_metadata[0] == 'F') { + /* we want to read the block type */ + current_member->block_type = block_metadata[0]; + } + else { + /* the last entry is incomplete and is therefore removed */ + sc_array_rewind (data_sections, data_sections->elem_count - 1); + /* current_member is freed if the whole array is freed */ + break; + } + + /* check format */ + if (block_metadata[P4EST_FILE_ARRAY_METADATA_BYTES + 1] != '\n') { + /* the last entry is incomplete and is therefore removed */ + sc_array_rewind (data_sections, data_sections->elem_count - 1); + break; + } + + /* process block metadata */ + block_metadata[P4EST_FILE_ARRAY_METADATA_BYTES + 1] = '\0'; + /* we cut off the block type specifier to read the data size */ + current_member->data_size = (size_t) sc_atol (&block_metadata[2]); + + /* read the user string */ + /* check '\n' to check the format */ + if (block_metadata[P4EST_FILE_FIELD_HEADER_BYTES - 1] != '\n') { + /* the last entry is incomplete and is therefore removed */ + sc_array_rewind (data_sections, data_sections->elem_count - 1); + break; + } + + /* null-terminate the user string of the current block */ + block_metadata[P4EST_FILE_FIELD_HEADER_BYTES - 1] = '\0'; + + /* copy the user string, '\0' was already set above */ + sc_strcopy (current_member->user_string, P4EST_FILE_USER_STRING_BYTES, + &block_metadata[P4EST_FILE_ARRAY_METADATA_BYTES + 2]); + P4EST_ASSERT (current_member->user_string + [P4EST_FILE_USER_STRING_BYTES - 1] == '\0'); + + /* get padding bytes of the current block */ + if (current_member->block_type == 'F') { + current_size = + (size_t) (p4est->global_num_quadrants * current_member->data_size); + } + else if (current_member->block_type == 'B') { + current_size = current_member->data_size; + } + else { + /* \ref p4est_file_read_block_metadata checks for valid block type */ + SC_ABORT_NOT_REACHED (); + } + p4est_file_get_padding_string (current_size, P4EST_FILE_BYTE_DIV, NULL, + &num_pad_bytes); + /* read padding bytes */ + eclass = sc_io_read_at (file, + current_position + + P4EST_FILE_FIELD_HEADER_BYTES + current_size, + block_metadata, num_pad_bytes, sc_MPI_BYTE, + &count); + *errcode = eclass; + if (!P4EST_FILE_IS_SUCCESS (eclass)) { + return p4est_file_error_cleanup (&file); + } + /* check '\n' in padding bytes */ + if (block_metadata[0] != '\n' + || block_metadata[num_pad_bytes - 1] != '\n') { + /* the last entry is incomplete and is therefore removed */ + P4EST_LERROR (P4EST_STRING + "_file_info: stop parsing file and discard last element " + "due to wrong padding format.\n"); + sc_array_rewind (data_sections, data_sections->elem_count - 1); + break; + } + current_position += + P4EST_FILE_FIELD_HEADER_BYTES + current_size + num_pad_bytes; + } + } + + /* replicate block metadata in parallel */ + long_header = (long) data_sections->elem_count; /* 0 on non-root */ + mpiret = sc_MPI_Bcast (&long_header, 1, sc_MPI_LONG, 0, p4est->mpicomm); + SC_CHECK_MPI (mpiret); + if (p4est->mpirank != 0) { + sc_array_resize (data_sections, (size_t) long_header); + } + mpiret = sc_MPI_Bcast (data_sections->array, + data_sections->elem_count * data_sections->elem_size, + sc_MPI_BYTE, 0, p4est->mpicomm); + SC_CHECK_MPI (mpiret); + + P4EST_ASSERT (P4EST_FILE_IS_SUCCESS (eclass)); + /* close the file with error checking */ + p4est_file_error_cleanup (&file); + + p4est_file_error_code (*errcode, errcode); + return 0; +} + +/** Converts a error code (MPI or libsc error) into a p4est_file error code. + * This function turns MPI error codes into MPI error classes if + * MPI IO is enabled. + * If errcode is already a p4est errorcode, it just copied to + * p4est_errcode. + * If MPI IO is not enabled, the function processes the errors outside + * of MPI but passes version 1.1 errors to MPI_Error_class. + * Furthermore, p4est_file functions can create \ref P4EST_FILE_ERR_COUNT + * as errcode what is also processed by this function. + * \param [in] errcode An errcode from a p4est_file function. + * \param [out] p4est_errcode Non-NULL pointer. Filled with matching + * error code on success. + * \return P4EST_FILE_ERR_SUCCESS on successful conversion. + * Other MPI error code otherwise. + */ +static int +p4est_file_error_code (int errcode, int *p4est_errcode) +{ + P4EST_ASSERT (p4est_errcode != NULL); + /* assertion on range of error code input */ + P4EST_ASSERT ((sc_MPI_SUCCESS <= errcode && errcode < sc_MPI_ERR_LASTCODE) + || (P4EST_FILE_ERR_SUCCESS <= errcode + && errcode < P4EST_FILE_ERR_LASTCODE)); + + /* copy p4est_file error codes that are not equal to + * libsc error codes */ + if (P4EST_FILE_ERR_SUCCESS <= errcode && errcode < P4EST_FILE_ERR_LASTCODE) { + /* errcode is a p4est_file errorcode */ + *p4est_errcode = errcode; + return P4EST_FILE_ERR_SUCCESS; + } + + /* map sc_io error codes to p4est error codes */ + switch (errcode) { + /* translate sc_io error codes that are the same as in p4est */ + case sc_MPI_SUCCESS: + *p4est_errcode = P4EST_FILE_ERR_SUCCESS; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_FILE: + *p4est_errcode = P4EST_FILE_ERR_FILE; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_NOT_SAME: + *p4est_errcode = P4EST_FILE_ERR_NOT_SAME; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_AMODE: + *p4est_errcode = P4EST_FILE_ERR_AMODE; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_NO_SUCH_FILE: + *p4est_errcode = P4EST_FILE_ERR_NO_SUCH_FILE; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_FILE_EXISTS: + *p4est_errcode = P4EST_FILE_ERR_FILE_EXIST; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_BAD_FILE: + *p4est_errcode = P4EST_FILE_ERR_BAD_FILE; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_ACCESS: + *p4est_errcode = P4EST_FILE_ERR_ACCESS; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_NO_SPACE: + *p4est_errcode = P4EST_FILE_ERR_NO_SPACE; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_QUOTA: + *p4est_errcode = P4EST_FILE_ERR_QUOTA; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_READ_ONLY: + *p4est_errcode = P4EST_FILE_ERR_READ_ONLY; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_FILE_IN_USE: + *p4est_errcode = P4EST_FILE_ERR_IN_USE; + return P4EST_FILE_ERR_SUCCESS; + case sc_MPI_ERR_UNKNOWN: + *p4est_errcode = P4EST_FILE_ERR_UNKNOWN; + return P4EST_FILE_ERR_SUCCESS; + + /* map sc_io error codes that are summarized in p4est */ + case sc_MPI_ERR_UNSUPPORTED_DATAREP: + case sc_MPI_ERR_UNSUPPORTED_OPERATION: + case sc_MPI_ERR_DUP_DATAREP: + case sc_MPI_ERR_CONVERSION: + *p4est_errcode = P4EST_FILE_ERR_IO; + return P4EST_FILE_ERR_SUCCESS; + + default: + /* errcode may be MPI version 1.1 error code */ + *p4est_errcode = P4EST_FILE_ERR_UNKNOWN; + return P4EST_FILE_ERR_SUCCESS; + } +} + +int +p4est_file_error_string (int errclass, char *string, int *resultlen) +{ + int retval; + const char *tstr = NULL; + + P4EST_ASSERT (resultlen != NULL); + P4EST_ASSERT (P4EST_FILE_ERR_SUCCESS <= errclass + && errclass < P4EST_FILE_ERR_LASTCODE); + + if (string == NULL || resultlen == NULL) { + return sc_MPI_ERR_ARG; + } + + /* handle p4est-define error codes */ + switch (errclass) { + case P4EST_FILE_ERR_SUCCESS: + tstr = "No p4est file error"; + break; + case P4EST_FILE_ERR_FORMAT: + tstr = "Wrong file format"; + break; + case P4EST_FILE_ERR_SECTION_TYPE: + tstr = "Valid non-matching section type"; + break; + case P4EST_FILE_ERR_CONN: + tstr = "Invalid serialized connectivity data"; + break; + case P4EST_FILE_ERR_P4EST: + tstr = "Invalid " P4EST_STRING " data"; + break; + case P4EST_FILE_ERR_IN_DATA: + tstr = "Invalid input data"; + break; + case P4EST_FILE_ERR_COUNT: + tstr = + "Read or write count error that is not classified as an other error"; + break; + + /* handle error codes as defined in libsc */ + case P4EST_FILE_ERR_FILE: + return sc_MPI_Error_string (sc_MPI_ERR_FILE, string, resultlen); + case P4EST_FILE_ERR_NOT_SAME: + return sc_MPI_Error_string (sc_MPI_ERR_NOT_SAME, string, resultlen); + case P4EST_FILE_ERR_AMODE: + return sc_MPI_Error_string (sc_MPI_ERR_AMODE, string, resultlen); + case P4EST_FILE_ERR_NO_SUCH_FILE: + return sc_MPI_Error_string (sc_MPI_ERR_NO_SUCH_FILE, string, resultlen); + case P4EST_FILE_ERR_FILE_EXIST: + return sc_MPI_Error_string (sc_MPI_ERR_FILE_EXISTS, string, resultlen); + case P4EST_FILE_ERR_BAD_FILE: + return sc_MPI_Error_string (sc_MPI_ERR_BAD_FILE, string, resultlen); + case P4EST_FILE_ERR_ACCESS: + return sc_MPI_Error_string (sc_MPI_ERR_ACCESS, string, resultlen); + case P4EST_FILE_ERR_NO_SPACE: + return sc_MPI_Error_string (sc_MPI_ERR_NO_SPACE, string, resultlen); + case P4EST_FILE_ERR_QUOTA: + return sc_MPI_Error_string (sc_MPI_ERR_QUOTA, string, resultlen); + case P4EST_FILE_ERR_READ_ONLY: + return sc_MPI_Error_string (sc_MPI_ERR_READ_ONLY, string, resultlen); + case P4EST_FILE_ERR_IN_USE: + return sc_MPI_Error_string (sc_MPI_ERR_FILE_IN_USE, string, resultlen); + case P4EST_FILE_ERR_UNKNOWN: + return sc_MPI_Error_string (sc_MPI_ERR_UNKNOWN, string, resultlen); + + default: + /* no valid p4est file error code */ + SC_ABORT_NOT_REACHED (); + } + P4EST_ASSERT (tstr != NULL); + + /* print into the output string */ + if ((retval = snprintf (string, sc_MPI_MAX_ERROR_STRING, "%s", tstr)) < 0) { + /* unless something goes against the current standard of snprintf */ + return sc_MPI_ERR_NO_MEM; + } + if (retval >= sc_MPI_MAX_ERROR_STRING) { + retval = sc_MPI_MAX_ERROR_STRING - 1; + } + *resultlen = retval; + + /* we have successfully placed a string in the output variables */ + return sc_MPI_SUCCESS; +} + +/* Write in an already opened file one or two sections. */ +p4est_file_context_t * +p4est_file_write_p4est (p4est_file_context_t * fc, p4est_t * p4est, + const char *quad_string, const char *quad_data_string, + int *errcode) +{ + p4est_gloidx_t *pertree; + sc_array_t arr; + sc_array_t *quads, *quad_data; + sc_array_t reshape; + + P4EST_ASSERT (fc != NULL); + P4EST_ASSERT (p4est != NULL); + + /* initialize */ + pertree = NULL; + + /* allocate memory for pertree */ + pertree = P4EST_ALLOC (p4est_gloidx_t, p4est->connectivity->num_trees + 1); + + /* get count per tree */ + p4est_comm_count_pertree (p4est, pertree); + + sc_array_init_data (&arr, pertree, + sizeof (p4est_gloidx_t) * + (p4est->connectivity->num_trees + 1), 1); + /* write count per tree to the file */ + fc = + p4est_file_write_block (fc, arr.elem_size, &arr, + P4EST_STRING " count per tree", errcode); + if (*errcode != P4EST_FILE_ERR_SUCCESS) { + P4EST_ASSERT (fc != NULL); + P4EST_FREE (pertree); + return NULL; + } + + quads = p4est_deflate_quadrants (p4est, &quad_data); + + /* p4est_file_write_field requires per rank local_num_quadrants many elements + * and therefore we group the data per local quadrant by type casting. + */ + sc_array_init_reshape (&reshape, quads, + P4EST_FILE_COMPRESSED_QUAD_SIZE, + p4est->local_num_quadrants); + + /** Write the current p4est to the file; we do not write the + * connectivity to disk because the connectivity is assumed to + * be known. + */ + fc = + p4est_file_write_field (fc, reshape.elem_size, &reshape, quad_string, + errcode); + if (*errcode != P4EST_FILE_ERR_SUCCESS) { + P4EST_ASSERT (fc == NULL); + /* first write call failed */ + sc_array_destroy (quads); + sc_array_destroy (quad_data); + return NULL; + } + + fc = + p4est_file_write_field (fc, quad_data->elem_size, quad_data, + quad_data_string, errcode); + P4EST_FREE (pertree); + sc_array_destroy (quads); + sc_array_destroy (quad_data); + + return fc; +} + +/** Convert read checkpoint data to a simulation p4est. + * + * \param [in] mpicomm MPI communicator of the p4est. + * \param [in] mpisize Number of MPI ranks. + * \param [in] conn connectivity used for the created + * p4est. + * \param [in] gfq Global first quadrant array that + * defines the partition of the + * created p4est. + * \param [in] pertree The cumulative count per tree. + * \param [in] quads An array of compressed quadrants + * that are used to create the new + * p4est. This means an array + * of (P4EST_DIM + 1) \ref p4est_qcoord_t + * that contains the quadrant coordinates + * succeeded by the quadrant level. + * \param [in] quad_data An array of quadrant data. This + * array must have as many elements + * as quadrants in the new p4est. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return A pointer to a newly allocated + * p4est that consists of the given + * quadrants and uses the given + * connectivity. + */ +static p4est_t * +p4est_file_data_to_p4est (sc_MPI_Comm mpicomm, int mpisize, + p4est_connectivity_t * conn, + const p4est_gloidx_t * gfq, + const p4est_gloidx_t * pertree, sc_array_t * quads, + sc_array_t * quad_data, int *errcode) +{ + p4est_t *ptemp; + sc_array_t quads_reshape; + + /* verify call convention and initialize error return */ + P4EST_ASSERT (conn != NULL); + P4EST_ASSERT (gfq != NULL); + P4EST_ASSERT (quads != NULL && + quads->elem_size == P4EST_FILE_COMPRESSED_QUAD_SIZE); + P4EST_ASSERT (quad_data != NULL); + P4EST_ASSERT (errcode != NULL); + *errcode = P4EST_FILE_ERR_P4EST; + + /* convert array interpretation for p4est_inflate_null */ + sc_array_init_reshape (&quads_reshape, quads, sizeof (p4est_qcoord_t), + (P4EST_DIM + 1) * quads->elem_count); + + ptemp = + p4est_inflate_null (mpicomm, conn, gfq, pertree, &quads_reshape, + quad_data, NULL); + if (ptemp != NULL) { + *errcode = P4EST_FILE_ERR_SUCCESS; + } + return ptemp; +} + +p4est_file_context_t * +p4est_file_read_p4est (p4est_file_context_t * fc, p4est_connectivity_t * conn, + size_t data_size, + p4est_t ** p4est, char *quad_string, + char *quad_data_string, int *errcode) +{ + int mpisize, mpiret; + p4est_topidx_t jt; + p4est_gloidx_t jq; + p4est_gloidx_t *gfq, *pertree; + sc_array_t quadrants, quad_data, pertree_arr; + p4est_qcoord_t *comp_quad; + + /* verify call convention */ + P4EST_ASSERT (fc != NULL); + P4EST_ASSERT (conn != NULL); + P4EST_ASSERT (p4est_connectivity_is_valid (conn)); + + /* initialize error return context */ + P4EST_ASSERT (p4est != NULL); + *p4est = NULL; + P4EST_ASSERT (errcode != NULL); + *errcode = P4EST_FILE_ERR_UNKNOWN; + gfq = NULL; + sc_array_init_size (&pertree_arr, + (conn->num_trees + 1) * sizeof (p4est_gloidx_t), 1); + sc_array_init (&quadrants, P4EST_FILE_COMPRESSED_QUAD_SIZE); + sc_array_init (&quad_data, data_size); + + /* temporary information */ + mpiret = sc_MPI_Comm_size (fc->mpicomm, &mpisize); + SC_CHECK_MPI (mpiret); + + /** Read data to construct the underlying p4est. + * One could also use p4est_{load,save} to read and write the p4est + * and use the p4est_file functions only for quadrant data that is + * stored externally. + */ + + /* read the count per tree */ + fc = + p4est_file_read_block (fc, pertree_arr.elem_size, + &pertree_arr, quad_string, errcode); + if (*errcode != P4EST_FILE_ERR_SUCCESS) { + /* first read call failed */ + /* this error occurs in particular for a wrong tree number */ + P4EST_ASSERT (fc == NULL); + goto p4est_read_file_p4est_end; + } + + pertree = (p4est_gloidx_t *) pertree_arr.array; + /* check the read pertree array */ + if (pertree[0] != 0) { + *errcode = P4EST_FILE_ERR_P4EST; + sc_array_reset (&pertree_arr); + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING "_file_read_" P4EST_STRING, errcode); + } + for (jt = 0; jt < conn->num_trees; ++jt) { + if (!(pertree[jt] <= pertree[jt + 1])) { + *errcode = P4EST_FILE_ERR_P4EST; + sc_array_reset (&pertree_arr); + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING "_file_read_" P4EST_STRING, + errcode); + } + } + if (fc->global_num_quadrants != pertree[conn->num_trees]) { + *errcode = P4EST_FILE_ERR_P4EST; + sc_array_reset (&pertree_arr); + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING "_file_read_" P4EST_STRING, errcode); + } + + gfq = P4EST_ALLOC (p4est_gloidx_t, mpisize + 1); + /** Compute a uniform global first quadrant array to use a uniform + * partition to read the data fields in parallel. + */ + p4est_comm_global_first_quadrant (fc->global_num_quadrants, mpisize, gfq); + + P4EST_ASSERT (gfq[mpisize] == pertree[conn->num_trees]); + + /* read the quadrants */ + fc = + p4est_file_read_field_ext (fc, gfq, quadrants.elem_size, &quadrants, + quad_string, errcode); + if (*errcode != P4EST_FILE_ERR_SUCCESS) { + P4EST_ASSERT (fc == NULL); + /* second read call failed */ + goto p4est_read_file_p4est_end; + } + + /* check the read quadrants */ + for (jq = 0; jq < (p4est_gloidx_t) quadrants.elem_count; ++jq) { + comp_quad = (p4est_qcoord_t *) sc_array_index (&quadrants, (size_t) jq); + if (!p4est_coordinates_is_valid (comp_quad, comp_quad[P4EST_DIM])) { + *errcode = P4EST_FILE_ERR_P4EST; + /* clean up local variables and open file context */ + P4EST_FREE (gfq); + sc_array_reset (&pertree_arr); + sc_array_reset (&quadrants); + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING "_file_read_" P4EST_STRING, + errcode); + } + } + + /* read the quadrant data */ + fc = + p4est_file_read_field_ext (fc, gfq, quad_data.elem_size, &quad_data, + quad_data_string, errcode); + if (*errcode != P4EST_FILE_ERR_SUCCESS) { + P4EST_ASSERT (fc == NULL); + /* third read call failed */ + goto p4est_read_file_p4est_end; + } + + /* create the p4est from the read data */ + *p4est = + p4est_file_data_to_p4est (fc->mpicomm, mpisize, conn, gfq, + (p4est_gloidx_t *) pertree_arr.array, + &quadrants, &quad_data, errcode); + P4EST_ASSERT ((p4est == NULL) == (*errcode != P4EST_FILE_ERR_SUCCESS)); + if (*errcode != P4EST_FILE_ERR_SUCCESS) { + /* clean up local variables and open file context */ + P4EST_FREE (gfq); + sc_array_reset (&pertree_arr); + sc_array_reset (&quadrants); + sc_array_reset (&quad_data); + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING "_file_read_" P4EST_STRING, errcode); + } + + /* clean up und return */ +p4est_read_file_p4est_end: + P4EST_FREE (gfq); + sc_array_reset (&pertree_arr); + sc_array_reset (&quadrants); + sc_array_reset (&quad_data); + return fc; +} + +p4est_file_context_t * +p4est_file_write_connectivity (p4est_file_context_t * fc, + p4est_connectivity_t * conn, + const char *conn_string, int *errcode) +{ + uint64_t conn_size = 0; + sc_array_t *conn_buffer, conn_size_arr, reshape; + + P4EST_ASSERT (fc != NULL); + P4EST_ASSERT (conn != NULL); + P4EST_ASSERT (conn_string != NULL); + P4EST_ASSERT (p4est_connectivity_is_valid (conn)); + + /* \ref p4est_connectivity_deflate aborts on errors */ + conn_buffer = p4est_connectivity_deflate (conn, P4EST_CONN_ENCODE_NONE); + + conn_size = (uint64_t) (conn_buffer->elem_size * conn_buffer->elem_count); + sc_array_init_data (&conn_size_arr, &conn_size, sizeof (uint64_t), 1); + fc = + p4est_file_write_block (fc, sizeof (size_t), + &conn_size_arr, + P4EST_STRING " connectivity size", errcode); + if (*errcode != P4EST_FILE_ERR_SUCCESS) { + P4EST_ASSERT (fc == NULL); + sc_array_destroy (conn_buffer); + return NULL; + } + + /* reshape the array to fit for \ref p4est_file_write_block */ + sc_array_init_reshape (&reshape, conn_buffer, + conn_buffer->elem_count * conn_buffer->elem_size, 1); + fc = + p4est_file_write_block (fc, reshape.elem_size, &reshape, + conn_string, errcode); + + /* clean up */ + sc_array_destroy (conn_buffer); + + return fc; +} + +p4est_file_context_t * +p4est_file_read_connectivity (p4est_file_context_t * fc, + p4est_connectivity_t ** conn, char *conn_string, + int *errcode) +{ + uint64_t read_conn_size; + size_t conn_size; + sc_array_t conn_size_arr; + sc_array_t conn_arr; + sc_array_t reshape; + + P4EST_ASSERT (fc != NULL); + P4EST_ASSERT (conn != NULL); + P4EST_ASSERT (conn_string != NULL); + + sc_array_init_data (&conn_size_arr, &read_conn_size, sizeof (uint64_t), 1); + /* get the connectivity size */ + fc = + p4est_file_read_block (fc, sizeof (uint64_t), &conn_size_arr, conn_string, + errcode); + if (*errcode != P4EST_FILE_ERR_SUCCESS) { + P4EST_ASSERT (fc == NULL); + return NULL; + } + + conn_size = (size_t) read_conn_size; + + sc_array_init_size (&conn_arr, conn_size, 1); + /* read the connectivity */ + fc = p4est_file_read_block (fc, conn_size, &conn_arr, conn_string, errcode); + if (*errcode != P4EST_FILE_ERR_SUCCESS) { + P4EST_ASSERT (fc == NULL); + return NULL; + } + + /* reshape the connectivity data for \ref p4est_connectivity_inflate */ + sc_array_init_reshape (&reshape, &conn_arr, conn_arr.elem_size, + sizeof (char)); + + /* create the connectivity from the read data */ + *conn = p4est_connectivity_inflate (&reshape); + + /* \ref p4est_connectivity_inflate returns NULL for an invalid + * connectivity. Therefore, we do not explicitly check for the + * validity of the returned connectivity. + */ + if (*conn == NULL) { + /* \ref p4est_connectivity_inflate failed due to wrong format */ + /* close, dealloc file and set specific error code */ + *errcode = P4EST_FILE_ERR_CONN; + P4EST_FILE_CHECK_NULL (*errcode, fc, + P4EST_STRING "_file_read_connectivity", errcode); + } + + /* clean up */ + sc_array_reset (&conn_arr); + + return fc; +} + +int +p4est_file_close (p4est_file_context_t * fc, int *errcode) +{ + P4EST_ASSERT (fc != NULL); + P4EST_ASSERT (errcode != NULL); + + int mpiret; + + mpiret = sc_io_close (&fc->file); + P4EST_FILE_CHECK_INT (mpiret, "Close file", errcode); + + if (fc->gfq_owned) { + P4EST_FREE (fc->global_first_quadrant); + } + P4EST_FREE (fc); + + p4est_file_error_code (*errcode, errcode); + return 0; +} + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ diff --git a/src/p4est_io.h b/src/p4est_io.h index e3280ea4b..b83778d54 100644 --- a/src/p4est_io.h +++ b/src/p4est_io.h @@ -22,6 +22,16 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/** \file p4est_io.h + * + * Provide functions to serialize/deserialize a forest. + * Some are used as building blocks for \ref p4est_load and \ref p4est_save. + * Others allow for saving and loading user-defined data to a parallel file. + * + * Furthermore, this module provides functions to write and read general data + * files associated with a p4est. + */ + #ifndef P4EST_IO_H #define P4EST_IO_H @@ -29,6 +39,41 @@ SC_EXTERN_C_BEGIN; +/** This parallel data file format is deprecated since we plan to release an + * updated version of it soon. You can still use \ref p4est_load and \ref + * p4est_save to read and write a p4est including the connectivity and + * quadrant data. However, you can not read and write external mesh associated + * data using a p4est function if you do not use the p4est_file functions. + * If you still want to use the p4est_file functions you can configure + * with --enable-file-deprecated or use the variable enable-file-deprecated + * in CMake. +*/ +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +#define P4EST_FILE_MAGIC_NUMBER "p4data0" /**< magic string for p4est data files */ +#define P4EST_FILE_METADATA_BYTES 96 /**< number of file metadata bytes */ +#define P4EST_FILE_MAGIC_BYTES 8 /**< number of bytes of the magic number */ +#define P4EST_FILE_VERSION_STR_BYTES 24 /**< number of bytes of the version string*/ +#define P4EST_FILE_ARRAY_METADATA_BYTES 14 /**< number of array metadata bytes */ +/* subtract 2 for '\n' at the beginning and end of the array metadata */ +#define P4EST_FILE_ARRAY_METADATA_CHARS (P4EST_FILE_ARRAY_METADATA_BYTES - 2) /**< number of array metadata chars */ +#define P4EST_FILE_BYTE_DIV 16 /**< All data blocks are padded to be divisible by this. */ +#define P4EST_FILE_MAX_NUM_PAD_BYTES (P4EST_FILE_BYTE_DIV + 1) /**< We enforce to pad in any + case and the padding string + needs to contain two + newline characters and + therefore this is the + maximal number of pad + bytes. */ +#define P4EST_FILE_USER_STRING_BYTES 48 /**< number of user string bytes */ +#define P4EST_FILE_FIELD_HEADER_BYTES (2 + P4EST_FILE_ARRAY_METADATA_BYTES + P4EST_FILE_USER_STRING_BYTES) + /**< number of bytes of one field header */ +#define P4EST_FILE_MAX_GLOBAL_QUAD 9999999999999999 /**< maximal number of global quadrants */ +#define P4EST_FILE_MAX_BLOCK_SIZE 9999999999999 /**< maximal number of block bytes */ +#define P4EST_FILE_MAX_FIELD_ENTRY_SIZE 9999999999999 /**< maximal number of bytes per field entry */ + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + /** Extract processor local quadrants' x y level data. * Optionally extracts the quadrant data as well into a separate array. * \param [in] p4est The forest is not modified. @@ -68,6 +113,640 @@ p4est_t *p4est_inflate (sc_MPI_Comm mpicomm, sc_array_t * quadrants, sc_array_t * data, void *user_pointer); +/** Create a new p4est based on serialized data. + * Its revision counter is set to zero. + * See p4est.h and p4est_communication.h for more information on parameters. + * In contrast to \ref p4est_inflate this function indicates soft errors + * by returning NULL. + * \param [in] mpicomm A valid MPI communicator. + * \param [in] connectivity This is the connectivity information that + * the forest is built with. Note that p4est + * does not take ownership of the memory. + * \param [in] global_first_quadrant First global quadrant on each proc and + * one beyond. Copied into global_first_quadrant. + * Local count on rank is gfq[rank + 1] - gfq[rank]. + * \param [in] pertree The cumulative quadrant counts per tree. + * \param [in] quadrants Array as returned by p4est_deflate_quadrants. + * \param [in] data Array as from p4est_deflate_quadrants or NULL. + * The elem_size of this array informs data_size. + * Its elem_count equals the number of local quads. + * \param [in] user_pointer Assign to the user_pointer member of the p4est. + * \return The newly created p4est with a zero revision counter. + * If the created p4est would not be valid, no p4est + * is created and the function returns NULL. + */ +p4est_t *p4est_inflate_null (sc_MPI_Comm mpicomm, + p4est_connectivity_t * connectivity, + const p4est_gloidx_t * + global_first_quadrant, + const p4est_gloidx_t * pertree, + sc_array_t * quadrants, + sc_array_t * data, + void *user_pointer); + +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +/** p4est data file format + * All p4est data files have 64 bytes file header section at the beginning of the file. + * The file header section is written to the file as string without NUL-termination + * (called string*) and is therefore readable in a text editor. + * + * File Header (96 bytes): + * 7 bytes magic number (p4data0) and 1 byte new line char. + * 23 bytes p4est version string* and 1 byte new line char. + * 47 bytes user string* and 1 byte new line char. + * 16 bytes number of global quadrants. + * + * The file header section is padded by 16 bytes consisting of 1 byte + * new line char succeeded by 14 bytes of spaces and 1 trailing byte + * new line char. + * + * The actual data is stored in arrays corresponding to a mesh of a p4est + * or in block sections that have a fixed user-defined size. The block + * sections are written and read on rank 0. + * One data field stores a fixed number of bytes of user- + * defined data per quadrant of a certain p4est. Therefore, one user-defined + * data field is of the size p4est->global_num_quadrants * data_size, where + * data_size is set by the user. The file format is partition independent. + * The data fields are padded such that the number of bytes for + * an array is divisible by 16. The padding also enforced for data blocks + * that have a size that is divisble by 16. + * The p4est data file consists of a variable number (including 0) of + * these two types of data sections. + * Every data section includes 64 bytes of section header written at the beginning + * by p4est. These 64 bytes are again written to the file as string* and can + * be read using a text editor. + * + * Data section Header (64 bytes): + * One byte data section type specific character (B for a block section and F for + * a data field), 1 byte space and 13 bytes size in number of bytes for a + * block section and data size per element in byte for a field section + * and one trailing byte new line char. + * 47 bytes user-defined string* and 1 byte new line char. + * + * The structure of p4est and p8est data files differs only by the magic number. + * + * The p4est metadata of a p4est data file can be accessed by \ref p4est_file_info(). + */ + +/** Opaque context used for writing a p4est data file. */ +typedef struct p4est_file_context p4est_file_context_t; + +/** Error values for p4est_file functions. + */ +typedef enum p4est_file_error +{ + P4EST_FILE_ERR_SUCCESS = sc_MPI_ERR_LASTCODE, /**< file function completed with success */ + P4EST_FILE_ERR_FILE, /**< invalid file handle */ + P4EST_FILE_ERR_NOT_SAME, /**< collective arg not identical */ + P4EST_FILE_ERR_AMODE, /**< access mode error */ + P4EST_FILE_ERR_NO_SUCH_FILE, /**< file does not exist */ + P4EST_FILE_ERR_FILE_EXIST, /**< file exists already */ + P4EST_FILE_ERR_BAD_FILE, /**< invalid file name */ + P4EST_FILE_ERR_ACCESS, /**< permission denied */ + P4EST_FILE_ERR_NO_SPACE, /**< not enough space */ + P4EST_FILE_ERR_QUOTA, /**< quota exceeded */ + P4EST_FILE_ERR_READ_ONLY, /**< read only file (system) */ + P4EST_FILE_ERR_IN_USE, /**< file currently open by other process */ + P4EST_FILE_ERR_IO, /**< other I/O error */ + P4EST_FILE_ERR_FORMAT, /**< read file has a wrong format */ + P4EST_FILE_ERR_SECTION_TYPE, /**< a valid non-matching section type */ + P4EST_FILE_ERR_CONN, /**< invalid serialized connectivity data */ + P4EST_FILE_ERR_P4EST, /**< invalid p4est data */ + P4EST_FILE_ERR_IN_DATA, /**< input data of file function is invalid */ + P4EST_FILE_ERR_COUNT, /**< read or write count error that was not + classified as a format error */ + P4EST_FILE_ERR_UNKNOWN, /**< unknown error */ + P4EST_FILE_ERR_LASTCODE /**< to define own error codes for + a higher level application + that is using p4est_file + functions */ +} +p4est_file_error_t; + +/** Begin writing file header and saving data blocks into a parallel file. + * + * This function creates a new file or overwrites an existing one. + * It is collective and creates the file on a parallel file system. + * It takes an (optional) pointer to write a header of given size. + * This function leaves the file open if MPI I/O is available. + * It is necessary to call \ref + * p4est_file_close (possibly after writing one or more data sets). + * The file is opened in a write-only mode. + * + * We add some basic metadata to the file. + * The file written contains the file header and data sets + * as specified by the open/write functions called. + * The file header consists of the metadata specified by p4est. + * + * The number of global quadrants must be less or equal + * \ref P4EST_FILE_MAX_GLOBAL_QUAD. + * + * It is the application's responsibility to write sufficient header + * information (cf. \ref p4est_file_write_block) to determine the number and + * size of the data sets if such information is not recorded and maintained + * externally. + * However, p4est makes some metadata accessible via + * \ref p4est_file_info. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in] p4est Valid forest. + * \param [in] filename Path to parallel file that is to be created. + * \param [in] user_string A user string that is written to the file header. + * Only \ref P4EST_FILE_USER_STRING_BYTES + * bytes without NUL-termination are + * written to the file. If the user gives less + * bytes the user_string in the file header is padded + * by spaces. + * \param [out] errcode An errcode that can be interpreted by + * \ref p4est_file_error_string. + * \return Newly allocated context to continue writing + * and eventually closing the file. NULL in + * case of error. + */ +p4est_file_context_t *p4est_file_open_create + (p4est_t * p4est, const char *filename, + const char *user_string, int *errcode); + +/** Open a file for reading and read its user string on rank zero. + * The user string is broadcasted to all ranks after reading. + * The file must exist and be at least of the size of the file header. + * + * If the file has wrong metadata the function reports the error using + * /ref P4EST_LERRORF, collectively close the file and deallocate + * the file context. In this case the function returns NULL on all ranks. + * The wrong file format or a wrong file header causes \ref P4EST_FILE_ERR_FORMAT + * as errcode. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in] p4est The forest must be of the same refinement + * pattern as the one used for writing the file. + * Its global number of quadrants must match. + * It is possible, however, to use a different + * partition or number of ranks from writing it. + * \param [in] filename The path to the file that is opened. + * \param [in,out] user_string At least \ref P4EST_FILE_USER_STRING_BYTES + * bytes. The user string is written + * to the passed array including padding spaces + * and a trailing NUL-termination. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return Newly allocated context to continue reading + * and eventually closing the file. NULL in + * case of error. + */ +p4est_file_context_t *p4est_file_open_read (p4est_t * p4est, + const char *filename, + char *user_string, int *errcode); + +/** Write a block section to an opened file. + * This function requires an opened file context. + * The block data and its metadata are written on rank 0. + * The number of block bytes must be less or equal + * \ref P4EST_FILE_MAX_BLOCK_SIZE. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [out] fc Context previously created by \ref + * p4est_file_open_create. + * \param [in] block_size The size of block in bytes. + * May be equal to 0. In this case the + * section header and the padding + * is still written. + * This function returns the passed fc + * parameter and sets errcode to + * \ref P4EST_FILE_ERR_SUCCESS if it is called + * for block_size == 0. + * \param [in] block_data A sc_array with one element and element size + * equal to \a block_size. + * The array points to the block data. The user is + * responsible for the validality of the block + * data. block_data can be NULL if + * block_size == 0. + * \param [in] user_string Maximal \ref P4EST_FILE_USER_STRING_BYTES bytes. + * These chars are written to the block + * header and padded to + * \ref P4EST_FILE_USER_STRING_BYTES - 1 chars + * by adding spaces. The '\0' is not written + * to the file. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return Return the input context to continue writing + * and eventually closing the file. The return + * value is NULL in case of error, then + * it also holds errcode != 0 and the file is + * tried to close and fc is freed. + */ +p4est_file_context_t *p4est_file_write_block (p4est_file_context_t * fc, + size_t block_size, + sc_array_t * block_data, + const char *user_string, + int *errcode); + +/** Read a header block from an opened file. + * This function requires an opened file context. + * The header data is read on rank 0. + * + * If the user does not have the header_size to call this function, the user + * can user \ref p4est_file_info to obtain the required information. + * + * The passed header_size is compared to the header_size stored in the file. + * If the values do not equal each other, the function reports details via + * /ref P4EST_LERRORF and closes and deallocate the file context. The return + * value in this case is NULL. + * If the block header information is not matching the passed parameters + * the function sets \ref P4EST_FILE_ERR_FORMAT for errcode. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [out] fc Context previously created by \ref + * p4est_file_open_create. + * \param [in] header_size The size of the header that is read. + * \param [in, out] header_data \a header_size allocated bytes in an sc_array + * with one element and \a header_size as element + * size. This data will be filled with the header + * data from file. If this is NULL it means that + * the current header block is skipped and the + * internal file pointer of the file context is + * set to the next data block. If current data + * block is not a header block, the file is closed + * and the file context is deallocated. Furthermore, + * in this case the function returns NULL and sets + * errcode to \ref P4EST_FILE_ERR_FORMAT. In case + * of skipping the header section \a header_size + * needs also to coincide with the header size + * given in the file. + * \param [in,out] user_string At least \ref P4EST_FILE_USER_STRING_BYTES bytes. + * Filled by the padded user string and + * a trailing NUL-termination char. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return Return the input context to continue reading + * and eventually closing the file. The return value + * is NULL if the function was called for + * header_size == 0. The return + * value is also NULL in case of error but then + * it also holds errcode != 0 and the file is + * tried to close and fc is freed. + */ +p4est_file_context_t *p4est_file_read_block (p4est_file_context_t * fc, + size_t header_size, + sc_array_t * header_data, + char *user_string, int *errcode); + +/** Write one (more) per-quadrant data set to a parallel output file. + * + * This function requires an opened file context. + * The data set is appended to the header/previously written data sets. + * This function writes a block of the size number of quadrants * data_size. + * + * The number of bytes per field entry must be less or equal + * \ref P4EST_FILE_MAX_FIELD_ENTRY_SIZE. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [out] fc Context previously created by \ref + * p4est_file_open_create. + * \param [in] quadrant_size The number of bytes per quadrant. This number + * must coincide with \a quadrant_data->elem_size. + * \param [in] quadrant_data An array of the length number of local quadrants + * with the element size equal to number of bytes + * written per quadrant. The quadrant data is expected + * to be stored according to the Morton order of + * the quadrants. For quadrant_data->elem_size == 0 + * the function writes an empty field. The section + * header and the padding is still written. + * In this case errcode is set + * to \ref P4EST_FILE_ERR_SUCCESS. + * \param [in] user_string An array of maximal \ref + * P4EST_FILE_USER_STRING_BYTES bytes that + * is written without the NUL-termination + * after the array-dependent metadata and before + * the actual data. If the char array is shorter the + * written char array will be padded to the + * right by spaces. The user_string is + * written on rank 0 and therefore also only + * required on rank 0. Can be NULL for other + * ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return Return the input context to continue writing + * and eventually closing the file. The return value + * is NULL if the function was called for + * quadrant_data->elem_size == 0. The return + * value is also NULL in case of error but then + * it also holds errcode != 0 and the file is + * tried to close and fc is freed. + */ +p4est_file_context_t *p4est_file_write_field (p4est_file_context_t * fc, + size_t quadrant_size, + sc_array_t * quadrant_data, + const char *user_string, + int *errcode); + +/** Read one (more) per-quadrant data set from a parallel input file. + * This function requires an opened file context. + * This function requires the appropriate number of readable bytes. + * In practice, the data size to read should match the size written. + * This function reports an error if the number of bytes to read is + * bigger than the dataset that corresponds to the processor. + * The data size to read is encoded by the element size of quadrant_data + * It is legal to close a file before all data sets have been read. + * + * The function closes and deallocates the file context and returns NULL + * if the bytes the user wants to read exceed the given file and/or + * the element size of the array given by quadrant_data->elem_size does not + * coincide with the element size according to the array metadata given in + * the file. + * + * If the block header information is not matching the passed parameters + * the function sets \ref P4EST_FILE_ERR_FORMAT for errcode. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in,out] fc Context previously created by \ref + * p4est_file_open_read (_ext). It keeps track + * of the data sets read one after another. + * \param [in] quadrant_size The number of bytes per quadrant. This number + * must coincide with \a quadrant_data->elem_size. + * \param [in,out] quadrant_data An array of the length number of local quadrants + * with the element size equal to number of bytes + * read per quadrant. The quadrant data is read + * according to the Morton order of the quadrants. + * \a quadrant_data->elem_size must coincide with + * the section data size in the file. + * quadrant_data == NULL means that the data is + * skipped and the internal file pointer is incremented. + * In the case of skipping \a quadrant_size is still + * checked using the corresponding value read from + * the file. If fc was opened by + * \ref p4est_file_open_read_ext and + * \a fc->global_first_quadrant was not set by the + * user, the function uses a uniform partition to read + * the data field in parallel. + * \a quadrant_data is resized by \ref sc_array_resize. + * \param [in,out] user_string At least \ref P4EST_FILE_USER_STRING_BYTES bytes. + * The user string is read on rank 0 and internally + * broadcasted to all ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program or if + * the function was called with quadrant_data == NULL. + * In case of error the file is tried to close + * and fc is freed. + */ +p4est_file_context_t *p4est_file_read_field (p4est_file_context_t * fc, + size_t quadrant_size, + sc_array_t * quadrant_data, + char *user_string, int *errcode); + +/** A data type that encodes the metadata of one data block in a p4est data file. + */ +typedef struct p4est_file_section_metadata +{ + char block_type; /**< 'H' (header) or 'F' (data file) */ + size_t data_size; /**< data size in bytes per array element ('F') + or of the header section ('H') */ + char user_string[P4EST_FILE_USER_STRING_BYTES]; /**< user string of the data section */ +} +p4est_file_section_metadata_t; + +/** Read metadata information of a file written by a matching forest. + * Matching refers to the global count of quadrants; partition is irrelevant. + * + * This function parses the given file on rank 0 and broadcasts the information + * on the number of data fields contained to all other ranks. Collective call. + * + * This function catches all I/O and file format errors and returns a valid MPI + * error class related to file handling. Errors are collectively synchronized. + * + * If the number of bytes that the user intend to read is larger than the number + * bytes left in the file, the function prints out an information about this + * situation using P4EST_LERROR. In this case the function reads the bytes + * that are possible to read but returns NULL to indicate an error. + * If the file or block header information is not matching the passed parameters + * the function sets \ref P4EST_FILE_ERR_FORMAT for errcode. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in] p4est A p4est that is only required for the + * MPI communicator, and to verify the + * global quadrant count found in the file. + * \param [in] filename Path to parallel file. + * \param [in,out] user_string At least \ref P4EST_FILE_USER_STRING_BYTES + * bytes. This array will be filled with the + * user string of the file after a successful + * call of this function. + * \param [in,out] data_sections After a successful function call this + * variable holds an array with a length + * corresponding to the number of data section + * in the file that are successfully found + * and seeked. The values in the array are the + * number of bytes of stored data per quadrant. + * Require elem_size->elem_size + * == sizeof (p4est_file_block_metadata_t) + * on input and preserve it on output. + * See p4est_file_block_metadata_t to obtain + * detailed information about the data blocks + * of the file. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return 0 for a successful call and -1 in case of + * an error. See also errcode argument. + */ +int p4est_file_info (p4est_t * p4est, const char *filename, + char *user_string, + sc_array_t * data_sections, + int *errcode); + +/** Turn p4est_file errcode into a string. + * + * \param [in] errclass An errcode that is output by a + * p4est_file function. + * \param [in,out] string At least sc_MPI_MAX_ERROR_STRING bytes. + * \param [out] resultlen Length of string on return. + * \return \ref P4EST_FILE_ERR_SUCCESS on success or + * something else on invalid arguments. + */ +int p4est_file_error_string (int errclass, + char *string, int *resultlen); + +/** Write a p4est to an opened file. + * This function does not write the connectvity of the p4est. + * If one want to write the connectivity, \ref p4est_file_write_connectivity + * can be used. + * + * \param [in,out] fc Context previously created by \ref + * p4est_file_open_create. It keeps track + * of the data sets written one after another. + * \param [in] p4est The p4est that is written to the file. + * \param [in] quad_string The string that is used as user string + * for quadrant section. + * An array of maximal \ref + * P4EST_FILE_USER_STRING_BYTES bytes that + * is written without the NUL-termination + * after the section-dependent metadata and before + * the actual data. If the char array is shorter the + * written char array will be padded to the + * right by spaces. The user_string is + * written on rank 0 and therefore also only + * required on rank 0. Can be NULL for other + * ranks. + * \param [in] quad_data_string The string that is used as user string + * for quadrant data section. + * An array of maximal \ref + * P4EST_FILE_USER_STRING_BYTES bytes that + * is written without the NUL-termination + * after the section-dependent metadata and before + * the actual data. If the char array is shorter the + * written char array will be padded to the + * right by spaces. The user_string is + * written on rank 0 and therefore also only + * required on rank 0. Can be NULL for other + * ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program. + * In case of error the file is tried to close + * and fc is freed. + */ +p4est_file_context_t *p4est_file_write_p4est (p4est_file_context_t * fc, + p4est_t * p4est, + const char *quad_string, + const char *quad_data_string, + int *errcode); + +/** Read a p4est to an opened file using the MPI communicator of \a fc. + * + * \param [in,out] fc Context previously created by \ref + * p4est_file_open_read (_ext). It keeps track + * of the data sets read one after another. + * \param [in] conn A connectivity that is used to create + * the \a p4est. + * \param [in] data_size The data size of the p4est that will + * be created by this function. + * \param [out] p4est The p4est that is created from the file. + * \param [in,out] quad_string The user string of the quadrant section. +* At least \ref P4EST_FILE_USER_STRING_BYTES bytes. + * The user string is read on rank 0 and internally + * broadcasted to all ranks. + * \param [in,out] quad_data_string The user string of the quadrant data section. + At least \ref P4EST_FILE_USER_STRING_BYTES bytes. + * The user string is read on rank 0 and internally + * broadcasted to all ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program. + * In case of error the file is tried to close + * and fc is freed. + */ +p4est_file_context_t *p4est_file_read_p4est (p4est_file_context_t * fc, + p4est_connectivity_t * conn, + size_t data_size, + p4est_t ** p4est, + char *quad_string, + char *quad_data_string, + int *errcode); + +/** Write a connectivity to an opened file. + * This function writes two block sections to the opened file. + * The first block contains the size of the serialized connectivity data + * and the second data block contains serialized connectivity. + * + * \param [in,out] fc Context previously created by \ref + * p4est_file_open_create. It keeps track + * of the data sets written one after another. + * \param [in] conn The connectivity that is written to the file. + * \param [in] conn_string The user string that written for the + * connectivity data block section. + * An array of maximal \ref + * P4EST_FILE_USER_STRING_BYTES bytes that + * is written without the NUL-termination + * after the section-dependent metadata and before + * the actual data. If the char array is shorter the + * written char array will be padded to the + * right by spaces. The user_string is + * written on rank 0 and therefore also only + * required on rank 0. Can be NULL for other + * ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program. + * In case of error the file is tried to close + * and fc is freed. + */ +p4est_file_context_t *p4est_file_write_connectivity (p4est_file_context_t * + fc, + p4est_connectivity_t * + conn, + const char *conn_string, + int *errcode); + +/** Read a connectivity from an opened file. + * This function reads two block sections from the opened file. + * The first block contains the size of the serialized connectivity data + * and the second data block contains serialized connectivity. + * + * \param [in,out] fc Context previously created by \ref + * p4est_file_open_read (_ext). It keeps track + * of the data sets written one after another. + * \param [out] conn The connectivity that is read from the file. + * \param [in,out] conn_string The user string that read for the + * connectivity data block section. + * At least \ref P4EST_FILE_USER_STRING_BYTES bytes. + * The user string is read on rank 0 and internally + * broadcasted to all ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program. + * In case of error the file is tried to close + * and fc is freed. + */ +p4est_file_context_t *p4est_file_read_connectivity (p4est_file_context_t * fc, + p4est_connectivity_t ** + conn, char *conn_string, + int *errcode); + +/** Close a file opened for parallel write/read and free the context. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in,out] fc Context previously created by \ref + * p4est_file_open_create or \ref + * p4est_file_open_read (_ext). Is freed. + * \param [out] errcode An errcode that can be interpreted by \ref + * p4est_file_error_string. + * \return 0 for a successful call and -1 in case of + * an error. See also errcode argument. + */ +int p4est_file_close (p4est_file_context_t * fc, + int *errcode); + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + SC_EXTERN_C_END; #endif /* !P4EST_IO_H */ diff --git a/src/p4est_iterate.c b/src/p4est_iterate.c index 45ac8b992..412cc6654 100644 --- a/src/p4est_iterate.c +++ b/src/p4est_iterate.c @@ -165,7 +165,7 @@ p4est_iter_tier_insert (sc_array_t * view, int level, size_t * next_tier, /* loop arg functions */ typedef struct p4est_iter_loop_args { - int alloc_size; /* large enough to accomodate strange + int alloc_size; /* large enough to accommodate strange corners/edges between trees */ #ifdef P4_TO_P8 int8_t loop_edge; /* should edge_iterate be run */ @@ -175,7 +175,7 @@ typedef struct p4est_iter_loop_args int level; int *level_num; /* an array that keeps track of which branch we take at each step in the - heirarchical search areas */ + hierarchical search areas */ int *quad_idx2; /* an indexing variable used in the iterate functions: passed as an argument to avoid using alloc/free @@ -184,7 +184,7 @@ typedef struct p4est_iter_loop_args local, one ghost), that contain the quadrants in each search area */ size_t **index; /* for each sidetype, the indices in quadrants - that form the bounds of the heirarchical + that form the bounds of the hierarchical search areas */ size_t *first_index; /* an indexing variable used in the iterate functions: passed as an @@ -564,7 +564,7 @@ p4est_iter_init_loop_corner (p4est_iter_loop_args_t * loop_args, /* When one iterate loop calls another, e.g. volume_iterate calls face_iterate, * the initial bounds for the new sides of the search need to be initialized. - * The whole heirarchy doesn't need to be copied, just the most recent bounds + * The whole hierarchy doesn't need to be copied, just the most recent bounds * from the correct starting sections (start_idx2). */ static void @@ -2517,7 +2517,7 @@ p4est_iter_init_face_from_volume (p4est_iter_face_args_t * args, /* given valid volume arguments, setup edge arguments for an edge search that * is called for an edge between four adjacent volumes: there are P4EST_DIM - * directions the edge can be oriented, and each direction can be run in oen + * directions the edge can be oriented, and each direction can be run in one * of two positioins, based on the child_ids of the four volumes surrounding * it. */ @@ -2740,7 +2740,7 @@ p4est_volume_iterate (p4est_iter_volume_args_t * args, void *user_data, count[type] = zindex[type][quad_idx2 + 1] - first_index[type]; } - /* if ther are no local quadrants, nothing to be done */ + /* if there are no local quadrants, nothing to be done */ if (!count[local]) { return; } diff --git a/src/p4est_iterate.h b/src/p4est_iterate.h index 08ff57007..47ec82a55 100644 --- a/src/p4est_iterate.h +++ b/src/p4est_iterate.h @@ -32,7 +32,6 @@ #ifndef P4EST_ITERATE_H #define P4EST_ITERATE_H -#include #include SC_EXTERN_C_BEGIN; diff --git a/src/p4est_lnodes.c b/src/p4est_lnodes.c index 0657817fc..cb42952ac 100644 --- a/src/p4est_lnodes.c +++ b/src/p4est_lnodes.c @@ -879,7 +879,7 @@ p4est_lnodes_corner_callback (p4est_iter_corner_info_t * info, void *Data) inode[1] = owner_qid; } - /* figure out if this is a remote corner or one for which we can determing + /* figure out if this is a remote corner or one for which we can determining * all touching and sharing procs */ has_local = 0; for (zz = 0; zz < count; zz++) { @@ -2840,10 +2840,8 @@ p4est_ghost_support_lnodes (p4est_t * p4est, p4est_lnodes_t * lnodes, startquad = qpn_offsets[nid]; endquad = qpn_offsets[nid + 1]; for (qid = startquad; qid < endquad; qid++) { - p4est_quadrant_t *q; - - q = p4est_quadrant_array_push (send_quads); - *q = node_to_quad[qid]; + (void) p4est_quadrant_array_push_copy + (send_quads, node_to_quad + qid); } } sc_array_sort (send_quads, p4est_quadrant_compare_piggy); @@ -2873,10 +2871,7 @@ p4est_ghost_support_lnodes (p4est_t * p4est, p4est_lnodes_t * lnodes, } } else { - p4est_quadrant_t *q2; - - q2 = p4est_quadrant_array_push (new_mirrors); - *q2 = *q; + (void) p4est_quadrant_array_push_copy (new_mirrors, q); } if (already_sent) { nquads--; diff --git a/src/p4est_lnodes.h b/src/p4est_lnodes.h index eea9f8aaf..3807b3f00 100644 --- a/src/p4est_lnodes.h +++ b/src/p4est_lnodes.h @@ -25,7 +25,6 @@ #ifndef P4EST_LNODES_H #define P4EST_LNODES_H -#include #include SC_EXTERN_C_BEGIN; diff --git a/src/p4est_mesh.c b/src/p4est_mesh.c index e41d747c0..70961a620 100644 --- a/src/p4est_mesh.c +++ b/src/p4est_mesh.c @@ -145,6 +145,15 @@ mesh_edge_process_inter_tree_edges (p8est_iter_edge_info_t * info, int goodones = 0; int edgeid_offset = mesh->local_num_quadrants + mesh->ghost_num_quadrants; + /* variables needed for adding edge-hanging corner information */ + int add_hedges = 0; + int cornerid; + int cid; + int cgoodones = 0; + int8_t *ccorners; + p4est_locidx_t *cquads; + int8_t *pccorner; + p4est_locidx_t *pcquad; /* sanity check for subedge_id */ P4EST_ASSERT (-1 <= subedge_id && subedge_id < 2); @@ -162,6 +171,16 @@ mesh_edge_process_inter_tree_edges (p8est_iter_edge_info_t * info, equads = P4EST_ALLOC (p4est_locidx_t, nAdjacentQuads); eedges = P4EST_ALLOC (int8_t, nAdjacentQuads); + ccorners = NULL; + cquads = NULL; + if (mesh->params.edgehanging_corners && + mesh->params.btype >= P8EST_CONNECT_CORNER) { + add_hedges = 1; + /* we have at most one hanging corner neighbor per edge-neighboring tree */ + cquads = P4EST_ALLOC (p4est_locidx_t, cz - 1); + ccorners = P4EST_ALLOC (int8_t, cz - 1); + } + P4EST_ASSERT (0 <= side1->treeid && side1->treeid < info->p4est->connectivity->num_trees); tree1 = p4est_tree_array_index (info->p4est->trees, side1->treeid); @@ -217,6 +236,18 @@ mesh_edge_process_inter_tree_edges (p8est_iter_edge_info_t * info, equads[goodones] = qid2; eedges[goodones] = P8EST_EDGES * localOri + (int) side2->edge; ++goodones; + if (add_hedges) { + /* store edge-hanging corner neighbor */ + int8_t subEdgeIdx = ((subedge_id ^ 1) + localOri) % 2; + qid2 = + side2->is.hanging.quadid[subEdgeIdx] + + (side2->is.hanging.is_ghost[subEdgeIdx] ? + mesh->local_num_quadrants : tree2->quadrants_offset); + cquads[cgoodones] = qid2; + ccorners[cgoodones] = + p8est_edge_corners[side2->edge][subEdgeIdx ^ 1]; + ++cgoodones; + } } else if (!side1->is_hanging && side2->is_hanging) { /* check if we have to swap hanging quads in order to store @@ -265,6 +296,23 @@ mesh_edge_process_inter_tree_edges (p8est_iter_edge_info_t * info, /* we have excluded between 0 and all quadrants. */ P4EST_ASSERT (0 <= goodones && goodones < nAdjacentQuads); + if (add_hedges) { + if (cgoodones > 0) { + /* Allocate and fill corner information in the mesh structure */ + cornerid = mesh_corner_allocate (mesh, cgoodones, &pcquad, &pccorner); + /* "link" to arrays encoding inter-tree corner-neighborhood */ + cid = p8est_edge_corners[side1->edge][subedge_id ^ 1]; + P4EST_ASSERT (mesh->quad_to_corner[P8EST_CHILDREN * qid1 + cid] == -1); + mesh->quad_to_corner[P8EST_CHILDREN * qid1 + cid] = + edgeid_offset + cornerid; + /* populate allocated memory */ + memcpy (pcquad, cquads, cgoodones * sizeof (p4est_locidx_t)); + memcpy (pccorner, ccorners, cgoodones * sizeof (int8_t)); + } + P4EST_FREE (cquads); + P4EST_FREE (ccorners); + } + if (goodones > 0) { /* Allocate and fill edge information in the mesh structure */ edgeid = mesh_edge_allocate (mesh, goodones, &pequad, &peedge); @@ -531,7 +579,7 @@ mesh_iter_edge (p8est_iter_edge_info_t * info, void *user_data) P4EST_ASSERT (cz > 0); P4EST_ASSERT (info->tree_boundary || cz == P4EST_HALF); - /* edge limits domain or is located on a face limitting the domain */ + /* edge limits domain or is located on a face limiting the domain */ /* sanity checks */ if (cz == 1) { P4EST_ASSERT (info->tree_boundary); @@ -554,56 +602,22 @@ mesh_iter_edge (p8est_iter_edge_info_t * info, void *user_data) return; } if (cz == 2) { + P4EST_ASSERT (info->tree_boundary); for (iz = 0; iz < cz; ++iz) { side1 = (p8est_iter_edge_side_t *) sc_array_index (&info->sides, iz); P4EST_ASSERT (0 <= side1->treeid && side1->treeid < info->p4est->connectivity->num_trees); P4EST_ASSERT (0 <= side1->edge && side1->edge < P8EST_EDGES); - - if (info->tree_boundary) { - if (side1->is_hanging) { - for (j = 0; j < 2; ++j) { - if (!side1->is.hanging.is_ghost[j]) { - mesh_edge_process_inter_tree_edges (info, side1, j, mesh, cz, - iz); - } - } - } - else { - if (!side1->is.full.is_ghost) { - mesh_edge_process_inter_tree_edges (info, side1, -1, mesh, cz, - iz); + if (side1->is_hanging) { + for (j = 0; j < 2; ++j) { + if (!side1->is.hanging.is_ghost[j]) { + mesh_edge_process_inter_tree_edges (info, side1, j, mesh, cz, iz); } } } else { - if (!side1->is_hanging) { - if (!side1->is.full.is_ghost) { - tree1 = - p4est_tree_array_index (info->p4est->trees, side1->treeid); - qid1 = side1->is.full.quadid + tree1->quadrants_offset; - - P4EST_ASSERT (0 <= qid1 && qid1 < mesh->local_num_quadrants); - P4EST_ASSERT - (mesh->quad_to_edge[P8EST_EDGES * qid1 + side1->edge] == -1); - - mesh->quad_to_edge[P8EST_EDGES * qid1 + side1->edge] = -3; - } - } - else { - for (j = 0; j < 2; ++j) { - if (!side1->is.hanging.is_ghost[j]) { - tree1 = - p4est_tree_array_index (info->p4est->trees, side1->treeid); - qid1 = side1->is.hanging.quadid[j] + tree1->quadrants_offset; - - P4EST_ASSERT (0 <= qid1 && qid1 < mesh->local_num_quadrants); - - P4EST_ASSERT - (mesh->quad_to_edge[P8EST_EDGES * qid1 + side1->edge] == -1); - mesh->quad_to_edge[P8EST_EDGES * qid1 + side1->edge] = -3; - } - } + if (!side1->is.full.is_ghost) { + mesh_edge_process_inter_tree_edges (info, side1, -1, mesh, cz, iz); } } } @@ -647,7 +661,7 @@ mesh_iter_edge (p8est_iter_edge_info_t * info, void *user_data) int8_t *peedge; p4est_locidx_t *pequad; - P4EST_ASSERT (!info->tree_boundary); + P4EST_ASSERT (!info->tree_boundary && cz == P4EST_HALF); side1 = (p8est_iter_edge_side_t *) sc_array_index (&info->sides, 0); P4EST_ASSERT (0 <= side1->treeid && @@ -657,8 +671,8 @@ mesh_iter_edge (p8est_iter_edge_info_t * info, void *user_data) memset (visited, 0, P4EST_HALF * sizeof (int8_t)); - /* TODO: someone has time double-checking this loop bound? */ - for (iz = 0; iz < ((cz + 1) >> 1); ++iz) { + /* search cz/2 == P4EST_HALF/2 pairs of opposing edges */ + for (iz = 0; iz < (cz >> 1); ++iz) { side1 = side2 = NULL; qid1 = -1; eid1 = -1; @@ -887,6 +901,68 @@ mesh_iter_edge (p8est_iter_edge_info_t * info, void *user_data) mesh->quad_to_edge[in_qtoe] = qid1; } } + if (mesh->params.edgehanging_corners && + mesh->params.btype >= P8EST_CONNECT_CORNER) { + /* add corner neighbor information across edge-hanging corner */ + int notk; + int cid; + p4est_locidx_t in_qtoc; + + for (k = 0; k < 2; ++k) { + notk = k ^ 1; + + /* do not record anything if both sides are ghost */ + if (side1->is.hanging.is_ghost[k] && + side2->is.hanging.is_ghost[notk]) { + continue; + } + + /* get qid1 and qid2 */ + if (!side1->is.hanging.is_ghost[k]) { + qid1 = side1->is.hanging.quadid[k] + qoffset; + P4EST_ASSERT (0 <= qid1 + && qid1 < mesh->local_num_quadrants); + } + else { + P4EST_ASSERT (side1->is.hanging.quad[k] != NULL); + P4EST_ASSERT (0 <= side1->is.hanging.quadid[k] && + side1->is.hanging.quadid[k] < + mesh->ghost_num_quadrants); + qid1 = + mesh->local_num_quadrants + side1->is.hanging.quadid[k]; + } + if (!side2->is.hanging.is_ghost[notk]) { + qid2 = side2->is.hanging.quadid[notk] + qoffset; + P4EST_ASSERT (0 <= qid2 + && qid2 < mesh->local_num_quadrants); + } + else { + P4EST_ASSERT (side2->is.hanging.quad[notk] != NULL); + P4EST_ASSERT (0 <= side2->is.hanging.quadid[notk] && + side2->is.hanging.quadid[notk] < + mesh->ghost_num_quadrants); + qid2 = + mesh->local_num_quadrants + + side2->is.hanging.quadid[notk]; + } + + /* write values */ + /* The corner lies on the edge, so we can use + * p8est_edge_corners as lookup. k indexes a proper corner, + * notk indexes the hanging corner we are interested in */ + cid = p8est_edge_corners[side1->edge][notk]; + if (!side1->is.hanging.is_ghost[k]) { + in_qtoc = P4EST_CHILDREN * qid1 + cid; + P4EST_ASSERT (mesh->quad_to_corner[in_qtoc] == -1); + mesh->quad_to_corner[in_qtoc] = qid2; + } + if (!side2->is.hanging.is_ghost[notk]) { + in_qtoc = P4EST_CHILDREN * qid2 + (cid ^ 7); + P4EST_ASSERT (mesh->quad_to_corner[in_qtoc] == -1); + mesh->quad_to_corner[in_qtoc] = qid1; + } + } + } } visited[j] = 1; side1 = side2 = NULL; @@ -1136,6 +1212,19 @@ p4est_mesh_memory_used (p4est_mesh_t * mesh) return all_memory; } +void +p4est_mesh_params_init (p4est_mesh_params_t * params) +{ + memset (params, 0, sizeof (p4est_mesh_params_t)); + + params->compute_level_lists = 0; + params->compute_tree_index = 0; + params->btype = P4EST_CONNECT_FACE; +#ifdef P4_TO_P8 + params->edgehanging_corners = 0; +#endif +} + p4est_mesh_t * p4est_mesh_new (p4est_t * p4est, p4est_ghost_t * ghost, p4est_connect_type_t btype) @@ -1147,6 +1236,24 @@ p4est_mesh_t * p4est_mesh_new_ext (p4est_t * p4est, p4est_ghost_t * ghost, int compute_tree_index, int compute_level_lists, p4est_connect_type_t btype) +{ + p4est_mesh_params_t params; + + /* initialize parameter struct to pass to mesh_new_params */ + p4est_mesh_params_init (¶ms); + params.btype = btype; + params.compute_level_lists = compute_level_lists; + params.compute_tree_index = compute_tree_index; +#ifdef P4_TO_P8 + params.edgehanging_corners = 0; +#endif + + return p4est_mesh_new_params (p4est, ghost, ¶ms); +} + +p4est_mesh_t * +p4est_mesh_new_params (p4est_t * p4est, p4est_ghost_t * ghost, + p4est_mesh_params_t * params) { int do_corner = 0; #ifdef P4_TO_P8 @@ -1159,27 +1266,37 @@ p4est_mesh_new_ext (p4est_t * p4est, p4est_ghost_t * ghost, p4est_mesh_t *mesh; /* check whether input condition for p4est is met */ - P4EST_ASSERT (p4est_is_balanced (p4est, btype)); + P4EST_ASSERT (p4est_is_balanced (p4est, params->btype)); mesh = P4EST_ALLOC_ZERO (p4est_mesh_t, 1); + /* store mesh creation parameters in mesh */ + if (params != NULL) { + mesh->params = *params; + params = NULL; + } + else { + p4est_mesh_params_init (&mesh->params); + } + /* number of local quadrants and number of local ghost cells */ lq = mesh->local_num_quadrants = p4est->local_num_quadrants; ng = mesh->ghost_num_quadrants = (p4est_locidx_t) ghost->ghosts.elem_count; /* decide which callback function have to be activated */ #ifdef P4_TO_P8 - if (btype >= P8EST_CONNECT_EDGE) { + if (mesh->params.btype >= P8EST_CONNECT_EDGE) { do_edge = 1; } #endif - if (btype >= P4EST_CONNECT_FULL) { + if (mesh->params.btype >= P4EST_CONNECT_FULL) { do_corner = 1; } - do_volume = compute_tree_index || compute_level_lists; + do_volume = mesh->params.compute_tree_index + || mesh->params.compute_level_lists; /* Optional map of tree index for each quadrant */ - if (compute_tree_index) { + if (mesh->params.compute_tree_index) { mesh->quad_to_tree = P4EST_ALLOC (p4est_topidx_t, lq); } @@ -1190,7 +1307,7 @@ p4est_mesh_new_ext (p4est_t * p4est, p4est_ghost_t * ghost, mesh->quad_to_half = sc_array_new (P4EST_HALF * sizeof (p4est_locidx_t)); /* Allocate optional per-level lists of quadrants */ - if (compute_level_lists) { + if (mesh->params.compute_level_lists) { mesh->quad_level = P4EST_ALLOC (sc_array_t, P4EST_QMAXLEVEL + 1); for (jl = 0; jl <= P4EST_QMAXLEVEL; ++jl) { @@ -1923,6 +2040,7 @@ get_corner_neighbors (p4est_t * p4est, p4est_ghost_t * ghost, return 0; } +/*** OUTDATED FUNCTION ***/ p4est_locidx_t p4est_mesh_get_neighbors (p4est_t * p4est, p4est_ghost_t * ghost, p4est_mesh_t * mesh, p4est_locidx_t curr_quad_id, diff --git a/src/p4est_mesh.h b/src/p4est_mesh.h index ee4030b60..2df4213d0 100644 --- a/src/p4est_mesh.h +++ b/src/p4est_mesh.h @@ -24,7 +24,19 @@ /** \file p4est_mesh.h * - * forest topology in a conventional mesh format + * Forest topology in a conventional mesh format. + * + * A typical workflow starts with \ref p4est_mesh_params_init to initialize a + * \ref p4est_mesh_params_t, followed by eventual user-dependent changes to the + * parameters. + * + * Next a \ref p4est_mesh_t is created with \ref p4est_mesh_new_params. + * + * Now, the user can create a \ref p4est_mesh_face_neighbor_t with + * \ref p4est_mesh_face_neighbor_init and loop over a quadrants face neighbors + * by repeated calls to \ref p4est_mesh_face_neighbor_next. + * + * Once done, the mesh has to be destroyed with \ref p4est_mesh_destroy. * * \ingroup p4est */ @@ -36,17 +48,34 @@ SC_EXTERN_C_BEGIN; +/** This structure contains the different parameters of mesh creation. + * A default instance can be initialized by calling \ref p4est_mesh_params_init + * and used for mesh creation by calling \ref p4est_mesh_new_params. */ +typedef struct +{ + int compute_tree_index; /**< Boolean to decide whether to + allocate and compute the + quad_to_tree list. */ + int compute_level_lists; /**< Boolean to decide whether to + compute the level lists in + quad_level. */ + p4est_connect_type_t btype; /**< Flag indicating the + connection types (face, edge, + corner) stored in the mesh. */ +} +p4est_mesh_params_t; + /** This structure contains complete mesh information on a 2:1 balanced forest. * It stores the locally relevant neighborhood, that is, all locally owned * quadrants and one layer of adjacent ghost quadrants and their owners. * * For each local quadrant, its tree number is stored in quad_to_tree. * The quad_to_tree array is NULL by default and can be enabled using - * \ref p4est_mesh_new_ext. + * \ref p4est_mesh_new_ext or \ref p4est_mesh_new_params. * For each ghost quadrant, its owner rank is stored in ghost_to_proc. * For each level, an array of local quadrant numbers is stored in quad_level. * The quad_level array is NULL by default and can be enabled using - * \ref p4est_mesh_new_ext. + * \ref p4est_mesh_new_ext or \ref p4est_mesh_new_params. * * The quad_to_quad list stores one value for each local quadrant's face. * This value is in 0..local_num_quadrants-1 for local quadrants, or in @@ -93,18 +122,18 @@ SC_EXTERN_C_BEGIN; * only happens on the domain boundary, which is necessarily a tree boundary. * Corner-neighbors for hanging nodes are assigned the value -1. * - * TODO: In case of an inter-tree corner neighbor relation in a brick-like - * situation (exactly one neighbor, diagonally opposite corner number), - * use the same encoding as for corners within a tree. + * The params struct describes the parameters the mesh was created with. + * For full control over the parameters, use \ref p8est_mesh_new_params for + * mesh creation. */ typedef struct { - p4est_locidx_t local_num_quadrants; - p4est_locidx_t ghost_num_quadrants; + p4est_locidx_t local_num_quadrants; /**< number of process-local quadrants */ + p4est_locidx_t ghost_num_quadrants; /**< number of ghost-layer quadrants */ p4est_topidx_t *quad_to_tree; /**< tree index for each local quad. - Is NULL by default, but may be - enabled by \ref p4est_mesh_new_ext. */ + Is NULL if compute_tree_index in + params is 0. */ int *ghost_to_proc; /**< processor for each ghost quad */ p4est_locidx_t *quad_to_quad; /**< one index for each of the 4 faces */ @@ -115,15 +144,21 @@ typedef struct The array has entries indexed by 0..P4EST_QMAXLEVEL inclusive that are arrays of local quadrant ids. - Is NULL by default, but may be - enabled by \ref p4est_mesh_new_ext. */ - - /* These members are NULL if corners are not requested in \ref p4est_mesh_new. */ - p4est_locidx_t local_num_corners; /* tree-boundary corners */ - p4est_locidx_t *quad_to_corner; /* 4 indices for each local quad */ - sc_array_t *corner_offset; /* local_num_corners + 1 entries */ - sc_array_t *corner_quad; /* corner_offset indexes into this */ - sc_array_t *corner_corner; /* and this one too (type int8_t) */ + Is NULL if compute_level_lists in + params is 0. */ + + /* These members are NULL if btype in params is < P8EST_CONNECT_CORNER and can + * be requested in \ref p4est_mesh_new. */ + p4est_locidx_t local_num_corners; /**< tree-boundary corners */ + p4est_locidx_t *quad_to_corner; /**< 4 indices for each local quad */ + sc_array_t *corner_offset; /**< local_num_corners + 1 entries */ + sc_array_t *corner_quad; /**< corner_offset indexes into this */ + sc_array_t *corner_corner; /**< and this one too (type int8_t) */ + + p4est_mesh_params_t params; /**< parameters the mesh was created + with, e.g. by passing them to + \ref p4est_mesh_new_ext or + \ref p4est_mesh_new_params */ } p4est_mesh_t; @@ -133,21 +168,22 @@ p4est_mesh_t; typedef struct { /* forest information */ - p4est_t *p4est; - p4est_ghost_t *ghost; - p4est_mesh_t *mesh; + p4est_t *p4est; /**< the forest */ + p4est_ghost_t *ghost; /**< the ghost layer of the forest */ + p4est_mesh_t *mesh; /**< a mesh derived from the forest */ /* quadrant information */ - p4est_topidx_t which_tree; - p4est_locidx_t quadrant_id; /* tree-local quadrant index */ - p4est_locidx_t quadrant_code; /* 4 * (quadrant_id + tree_offset) */ + p4est_topidx_t which_tree; /**< the current tree index */ + p4est_locidx_t quadrant_id; /**< tree-local quadrant index */ + p4est_locidx_t quadrant_code; /**< 4 * (quadrant_id + tree_offset) */ /* neighbor information */ - int face; /* Face number in 0..3. */ - int subface; /* Hanging neighbor number in 0..1. */ + int face; /**< Face number in 0..3. */ + int subface; /**< Hanging neighbor number in 0..1. */ /* internal information */ - p4est_locidx_t current_qtq; + p4est_locidx_t current_qtq; /**< track index of current neighboring + quadrant */ } p4est_mesh_face_neighbor_t; @@ -157,9 +193,14 @@ p4est_mesh_face_neighbor_t; */ size_t p4est_mesh_memory_used (p4est_mesh_t * mesh); +/** Initialize a default p4est_mesh_params_t structure. + * The parameters are set to create the most basic mesh structure, without + * tree index and level lists and considering only face connections. */ +void p4est_mesh_params_init (p4est_mesh_params_t * params); + /** Create a p4est_mesh structure. * This function does not populate the quad_to_tree and quad_level fields. - * To populate them, use \ref p4est_mesh_new_ext. + * To populate them, use \ref p4est_mesh_new_params. * \param [in] p4est A forest that is fully 2:1 balanced. * \param [in] ghost The ghost layer created from the provided p4est. * \param [in] btype Determines the highest codimension of neighbors. @@ -169,6 +210,17 @@ p4est_mesh_t *p4est_mesh_new (p4est_t * p4est, p4est_ghost_t * ghost, p4est_connect_type_t btype); +/** Create a new mesh. + * \param [in] p4est A forest that is fully 2:1 balanced. + * \param [in] ghost The ghost layer created from the provided p4est. + * \param [in] params The mesh creation parameters. If NULL, the function + * defaults to the parameters of \ref p4est_mesh_params_init. + * \return A fully allocated mesh structure. + */ +p4est_mesh_t *p4est_mesh_new_params (p4est_t * p4est, + p4est_ghost_t * ghost, + p4est_mesh_params_t * params); + /** Destroy a p4est_mesh structure. * \param [in] mesh Mesh structure previously created by p4est_mesh_new. */ @@ -187,6 +239,7 @@ p4est_quadrant_t *p4est_mesh_get_quadrant (p4est_t * p4est, p4est_mesh_t * mesh, p4est_locidx_t qid); +/*** OUTDATED FUNCTION ***/ /** Lookup neighboring quads of quadrant in a specific direction. * \param [in] p4est Forest to be worked with. * \param [in] ghost Ghost layer. @@ -263,6 +316,9 @@ p4est_quadrant_t *p4est_mesh_quadrant_cumulative (p4est_t * p4est, /** Initialize a mesh neighbor iterator by quadrant index. * \param [out] mfn A p4est_mesh_face_neighbor_t to be initialized. + * \param [in] p4est Forest to be worked with. + * \param [in] ghost Ghost layer of the forest. + * \param [in] mesh A mesh derived from the forest. * \param [in] which_tree Tree of quadrant whose neighbors are looped over. * \param [in] quadrant_id Index relative to which_tree of quadrant. */ @@ -276,6 +332,9 @@ void p4est_mesh_face_neighbor_init2 (p4est_mesh_face_neighbor_t /** Initialize a mesh neighbor iterator by quadrant pointer. * \param [out] mfn A p4est_mesh_face_neighbor_t to be initialized. + * \param [in] p4est Forest to be worked with. + * \param [in] ghost Ghost layer of the forest. + * \param [in] mesh A mesh derived from the forest. * \param [in] which_tree Tree of quadrant whose neighbors are looped over. * \param [in] quadrant Pointer to quadrant contained in which_tree. */ @@ -292,7 +351,8 @@ void p4est_mesh_face_neighbor_init (p4est_mesh_face_neighbor_t * \param [out] ntree If not NULL, the tree number of the neighbor. * \param [out] nquad If not NULL, the quadrant number within tree. * For ghosts instead the number in ghost layer. - * \param [out] nface If not NULL, neighbor's face as in p4est_mesh_t. + * \param [out] nface If not NULL, neighbor's face encoding as in + quad_to_face array of p4est_mesh_t. * \param [out] nrank If not NULL, the owner process of the neighbor. * \return Either a real quadrant or one from the ghost layer. * Returns NULL when the iterator is done. diff --git a/src/p4est_nodes.c b/src/p4est_nodes.c index 02008f4bb..d0a420178 100644 --- a/src/p4est_nodes.c +++ b/src/p4est_nodes.c @@ -449,12 +449,9 @@ p4est_node_canonicalize (p4est_t * p4est, p4est_topidx_t treeid, static int p4est_nodes_foreach (void **item, const void *u) { - const sc_hash_array_data_t *internal_data = - (const sc_hash_array_data_t *) u; - const p4est_locidx_t *new_node_number = - (const p4est_locidx_t *) internal_data->user_data; + const p4est_locidx_t *new_node_number = (const p4est_locidx_t *) u; - *item = (void *) (size_t) new_node_number[(size_t) * item]; + *item = (void *) (size_t) new_node_number[(size_t) *item]; return 1; } @@ -732,10 +729,10 @@ p4est_nodes_new (p4est_t * p4est, p4est_ghost_t * ghost) } /* Re-synchronize hash array and local nodes */ - save_user_data = indep_nodes->internal_data.user_data; - indep_nodes->internal_data.user_data = new_node_number; - sc_hash_foreach (indep_nodes->h, p4est_nodes_foreach); - indep_nodes->internal_data.user_data = save_user_data; + save_user_data = indep_nodes->user_data; + indep_nodes->user_data = new_node_number; + sc_hash_array_foreach (indep_nodes, p4est_nodes_foreach); + indep_nodes->user_data = save_user_data; for (il = 0; il < num_local_nodes; ++il) { P4EST_ASSERT (local_nodes[il] >= 0 && local_nodes[il] < num_indep_nodes); local_nodes[il] = new_node_number[local_nodes[il]]; @@ -885,7 +882,7 @@ p4est_nodes_new (p4est_t * p4est, p4est_ghost_t * ghost) k = probe_status.MPI_SOURCE; peer = peers + k; P4EST_ASSERT (k != rank && peer->expect_query); - mpiret = MPI_Get_count (&probe_status, MPI_BYTE, &byte_count); + mpiret = sc_MPI_Get_count (&probe_status, MPI_BYTE, &byte_count); SC_CHECK_MPI (mpiret); P4EST_ASSERT (byte_count % first_size == 0); elem_count = byte_count / (int) first_size; @@ -1169,7 +1166,7 @@ p4est_nodes_new (p4est_t * p4est, p4est_ghost_t * ghost) k = probe_status.MPI_SOURCE; peer = peers + k; P4EST_ASSERT (k != rank && peer->expect_reply); - mpiret = MPI_Get_count (&probe_status, MPI_BYTE, &byte_count); + mpiret = sc_MPI_Get_count (&probe_status, MPI_BYTE, &byte_count); SC_CHECK_MPI (mpiret); sc_array_resize (&peer->recv_second, (size_t) byte_count); mpiret = MPI_Recv (peer->recv_second.array, byte_count, MPI_BYTE, @@ -1263,9 +1260,9 @@ p4est_nodes_new (p4est_t * p4est, p4est_ghost_t * ghost) #ifdef P4EST_ENABLE_MPI /* Wait and close all send requests. */ if (send_requests.elem_count > 0) { - mpiret = MPI_Waitall ((int) send_requests.elem_count, - (MPI_Request *) send_requests.array, - MPI_STATUSES_IGNORE); + mpiret = sc_MPI_Waitall ((int) send_requests.elem_count, + (MPI_Request *) send_requests.array, + MPI_STATUSES_IGNORE); SC_CHECK_MPI (mpiret); } nodes->shared_offsets = shared_offsets; diff --git a/src/p4est_nodes.h b/src/p4est_nodes.h index 690374aa7..3aee1c2aa 100644 --- a/src/p4est_nodes.h +++ b/src/p4est_nodes.h @@ -25,7 +25,6 @@ #ifndef P4EST_NODES_H #define P4EST_NODES_H -#include #include SC_EXTERN_C_BEGIN; diff --git a/src/p4est_plex.c b/src/p4est_plex.c index f6c4054f3..d5d21089e 100644 --- a/src/p4est_plex.c +++ b/src/p4est_plex.c @@ -99,6 +99,190 @@ p4est_gloidx_pair_compare (const void *a, const void *b) } } +static void +p4est_coordinates_canonicalize (p4est_t * p4est, + p4est_locidx_t tree_id, + const p4est_qcoord_t these_coords[], + p4est_locidx_t * neigh_tree_id, + p4est_qcoord_t neigh_coords[]) +{ + int face_code, n_outside; + p4est_connect_type_t neigh_type; + int neigh_index; + sc_array_t neigh_transforms; + + *neigh_tree_id = tree_id; + face_code = 0; + n_outside = 0; + for (int d = 0; d < P4EST_DIM; d++) { + neigh_coords[d] = these_coords[d]; + P4EST_ASSERT (0 <= these_coords[d] && these_coords[d] <= P4EST_ROOT_LEN); + face_code |= (these_coords[d] == 0) ? (1 << (2 * d)) : 0; + face_code |= (these_coords[d] == P4EST_ROOT_LEN) ? (1 << (2 * d + 1)) : 0; + n_outside += (these_coords[d] == 0 || these_coords[d] == P4EST_ROOT_LEN); + } + switch (n_outside) { + case 0: + return; + case 1: + neigh_type = P4EST_CONNECT_FACE; + neigh_index = SC_LOG2_8 (face_code); + break; +#ifdef P4_TO_P8 + case 2: + neigh_type = P8EST_CONNECT_EDGE; + neigh_index = -1; + for (int e = 0; e < P8EST_EDGES; e++) { + if ((face_code & (1 << p8est_edge_faces[e][0])) && + (face_code & (1 << p8est_edge_faces[e][1]))) { + neigh_index = e; + break; + } + } + P4EST_ASSERT (neigh_index >= 0); + break; +#endif + case P4EST_DIM: + neigh_type = P4EST_CONNECT_CORNER; + neigh_index = 0; + for (int d = 0; d < P4EST_DIM; d++) { + neigh_index += (face_code & (1 << (2 * d + 1))) ? (1 << d) : 0; + } + break; + default: + SC_ABORT_NOT_REACHED (); + } + sc_array_init (&neigh_transforms, sizeof (p4est_neighbor_transform_t)); + p4est_connectivity_get_neighbor_transforms (p4est->connectivity, tree_id, + neigh_type, neigh_index, + &neigh_transforms); + /* skip the first neighbor which is self */ + for (size_t iz = 1; iz < neigh_transforms.elem_count; iz++) { + p4est_neighbor_transform_t *nt = + (p4est_neighbor_transform_t *) sc_array_index (&neigh_transforms, iz); + p4est_qcoord_t trans_coords[P4EST_DIM]; + + if (nt->neighbor > *neigh_tree_id) { + continue; + } + p4est_neighbor_transform_coordinates (nt, these_coords, trans_coords); + if (nt->neighbor < *neigh_tree_id + || p4est_coordinates_compare (trans_coords, neigh_coords) < 0) { + *neigh_tree_id = nt->neighbor; + for (int d = 0; d < P4EST_DIM; d++) { + neigh_coords[d] = trans_coords[d]; + } + } + } + sc_array_reset (&neigh_transforms); +} + +/* Given a quadrant, local or ghost, and its + + - face code and + - quad_to_local map, + + compute the vertex coordinates at the center of each + local interface. The vertex coordinates can be ambiguous + on a periodic or mesh with disconnected vertices, so + this function canonicalizes the results so that all + quadrants that share a node will agree on its coordinates. + + We even compute the coordinates of mesh points that are not vertices (at + their centers), because the mesh point may be a parent to a vertex, + and that vertex will inherit its coordinates +*/ +static void +quadrant_get_local_coordinates (p4est_t * p4est, p4est_locidx_t tree_id, + p4est_quadrant_t * quad, + p4est_lnodes_code_t face_code, + double coords[][3]) +{ +#ifndef P4_TO_P8 + int dim_limits[3] = { 0, 4, 8 }; +#else + int dim_limits[4] = { 0, 6, 18, 26 }; +#endif + int has_hanging, hanging[3][12]; + p4est_quadrant_t p; + /* initialize each local dof with (tree_id, qcoord) coordinates */ + p4est_qcoord_t h = P4EST_QUADRANT_LEN (quad->level), H; + + P4EST_QUADRANT_INIT (&p); +#ifndef P4_TO_P8 + has_hanging = p4est_lnodes_decode (face_code, &hanging[0][0]); +#else + has_hanging = + p8est_lnodes_decode (face_code, &hanging[0][0], &hanging[1][0]); +#endif + has_hanging |= lnodes_decode2 (face_code, &hanging[P4EST_DIM - 1][0]); + H = h; + if (has_hanging) { + p4est_quadrant_parent (quad, &p); + H = P4EST_QUADRANT_LEN (p.level); + } + + /* compute the coordinates in this tree */ + for (int dim = 0; dim < P4EST_DIM; dim++) { + int vstart = dim_limits[P4EST_DIM - dim - 1]; + int vend = dim_limits[P4EST_DIM - dim]; + const int *vhanging = &(hanging[P4EST_DIM - dim - 1][0]); + + for (int v = 0; v < vend - vstart; v++) { + p4est_qcoord_t these_coords[3]; + p4est_qcoord_t neigh_coords[3]; + p4est_quadrant_t *q = (has_hanging && (vhanging[v] >= 0)) ? &p : quad; + p4est_qcoord_t vh = (has_hanging && (vhanging[v] >= 0)) ? H : h; + p4est_locidx_t neigh_tree_id; + + these_coords[0] = q->x; + these_coords[1] = q->y; +#ifdef P4_TO_P8 + these_coords[2] = q->z; +#endif + + switch (dim) { + case 0: /* corners */ + for (int d = 0; d < P4EST_DIM; d++) { + these_coords[d] += (v & (1 << d)) ? vh : 0; + } + break; + case (P4EST_DIM - 1): /* faces */ + for (int d = 0; d < P4EST_DIM; d++) { + these_coords[d] += (d == v / 2) ? ((v & 1) * vh) : vh / 2; + } + break; +#ifdef P4_TO_P8 + case 1: /* edges */ + { + int lo_dir[3] = { 1, 0, 0 }; + for (int d = 0; d < P4EST_DIM; d++) { + these_coords[d] += (d == v / 4) ? vh / 2 : + vh * ((d == lo_dir[v / 4]) ? (v & 1) : ((v & 2) >> 1)); + } + } + break; +#endif + default: + SC_ABORT_NOT_REACHED (); + } + for (int d = 0; d < P4EST_DIM; d++) { + P4EST_ASSERT (0 <= these_coords[d] + && these_coords[d] <= P4EST_ROOT_LEN); + } + p4est_coordinates_canonicalize (p4est, tree_id, these_coords, + &neigh_tree_id, neigh_coords); + p4est_qcoord_to_vertex (p4est->connectivity, neigh_tree_id, + neigh_coords[0], neigh_coords[1], +#ifdef P4_TO_P8 + neigh_coords[2], +#endif + &coords[v + vstart][0]); + } + } + +} + static void mark_parent (p4est_locidx_t qid, int ctype_int, p4est_lnodes_code_t * F, p4est_locidx_t * quad_to_local, int8_t * is_parent, @@ -560,6 +744,7 @@ p4est_get_plex_data_int (p4est_t * p4est, p4est_ghost_t * ghost, p4est_locidx_t qid; int v, V = lnodes->vnodes; sc_array_t *is_parent, *node_dim; + sc_array_t *node_coords; int ctype_int = p4est_connect_type_int (P4EST_CONNECT_FULL); p4est_locidx_t num_global, num_global_plus_children, last_global, *child_offsets; @@ -850,6 +1035,59 @@ p4est_get_plex_data_int (p4est_t * p4est, p4est_ghost_t * ghost, } } + /* loop over quads: + * - compute coordinates for each node + */ + node_coords = sc_array_new_size (3 * sizeof (double), num_global); + for (qid = 0, t = flt; t <= llt; t++) { + p4est_tree_t *tree = p4est_tree_array_index (p4est->trees, t); + sc_array_t *quadrants = &(tree->quadrants); + p4est_locidx_t num_quads = (p4est_locidx_t) quadrants->elem_count; + + for (p4est_locidx_t il = 0; il < num_quads; il++, qid++) { + p4est_quadrant_t *q = + p4est_quadrant_array_index (quadrants, (size_t) il); + p4est_lnodes_code_t fc = F[qid]; + double q_node_coords[P4EST_INSUL][3]; + + quadrant_get_local_coordinates (p4est, t, q, fc, q_node_coords); + for (int v = 0; v < V; v++) { + double *v_coords = + (double *) sc_array_index (node_coords, + (size_t) quad_to_local[qid * V + v]); + + for (int d = 0; d < 3; d++) { + v_coords[d] = q_node_coords[v][d]; + } + } + } + } + if (overlap) { + for (t = 0; t < p4est->connectivity->num_trees; t++) { + p4est_locidx_t il, istart = ghost->tree_offsets[t]; + p4est_locidx_t iend = ghost->tree_offsets[t + 1]; + + for (il = istart; il < iend; il++) { + p4est_quadrant_t *q = + p4est_quadrant_array_index (&ghost->ghosts, (size_t) il); + p4est_locidx_t qid = il + Klocal; + p4est_lnodes_code_t fc = F[qid]; + double q_node_coords[P4EST_INSUL][3]; + + quadrant_get_local_coordinates (p4est, t, q, fc, q_node_coords); + for (int v = 0; v < V; v++) { + double *v_coords = + (double *) sc_array_index (node_coords, + (size_t) quad_to_local[qid * V + v]); + + for (int d = 0; d < 3; d++) { + v_coords[d] = q_node_coords[v][d]; + } + } + } + } + } + /* loop over quads: * - where quad_to_local refers to a parent, make it refer to the correct * child @@ -1402,176 +1640,29 @@ p4est_get_plex_data_int (p4est_t * p4est, p4est_ghost_t * ghost, } #endif /* compute coordinates */ - for (qid = 0, t = flt; t <= llt; t++) { - p4est_tree_t *tree = p4est_tree_array_index (p4est->trees, t); - sc_array_t *quadrants = &(tree->quadrants); - p4est_locidx_t num_quads = (p4est_locidx_t) quadrants->elem_count; + for (p4est_locidx_t v = 0; + v < dim_offsets[ctype_int + 1] - dim_offsets[ctype_int]; v++) { + p4est_locidx_t lid = plex_to_local[v + dim_offsets[ctype_int]] - K; + p4est_locidx_t parent; + const double *n_coords; + double *v_coords = &coords[v * 3]; - for (il = 0; il < num_quads; il++, qid++) { - p4est_quadrant_t *q = - p4est_quadrant_array_index (quadrants, (size_t) il); - p4est_qcoord_t h = P4EST_QUADRANT_LEN (q->level); - int vstart, vend; + P4EST_ASSERT (lid >= 0); - vstart = dim_limits[P4EST_DIM - 1]; - vend = dim_limits[P4EST_DIM]; - for (v = vstart; v < vend; v++) { - int corner = v - vstart; - p4est_locidx_t vid = - local_to_plex[quad_to_local[qid * V + v] + K] - - dim_offsets[P4EST_DIM]; - p4est_locidx_t vid2 = - (quad_to_local_orig != - NULL) ? (local_to_plex[quad_to_local_orig[qid * V + v] + K] - - dim_offsets[P4EST_DIM]) : vid; - double vcoord[3]; - - p4est_qcoord_to_vertex (p4est->connectivity, t, - q->x + ((corner & 1) ? h : 0), - q->y + ((corner & 2) ? h : 0), -#ifdef P4_TO_P8 - q->z + ((corner & 4) ? h : 0), -#endif - vcoord); - coords[3 * vid + 0] = vcoord[0]; - coords[3 * vid + 1] = vcoord[1]; - coords[3 * vid + 2] = vcoord[2]; - - if (vid2 != vid) { - p4est_quadrant_t p; - - p4est_quadrant_parent (q, &p); - p4est_qcoord_to_vertex (p4est->connectivity, t, - p.x + ((corner & 1) ? (2 * h) : 0), - p.y + ((corner & 2) ? (2 * h) : 0), -#ifdef P4_TO_P8 - p.z + ((corner & 4) ? (2 * h) : 0), -#endif - vcoord); - coords[3 * vid2 + 0] = vcoord[0]; - coords[3 * vid2 + 1] = vcoord[1]; - coords[3 * vid2 + 2] = vcoord[2]; - } - } + parent = *((p4est_locidx_t *) sc_array_index (child_to_parent, lid)); + if (parent >= 0) { + n_coords = (double *) sc_array_index (node_coords, parent); } - } - /* interpolate children coordinates from parent vertices */ - for (il = 0; il < num_global; il++) { - p4est_locidx_t pid, nid; - int8_t isp; - - nid = il + K; - pid = local_to_plex[nid]; - isp = *((int8_t *) sc_array_index (is_parent, il)); - if (isp) { - p4est_locidx_t cvert = child_offsets[il + 1] - 1, poff, vid; - int8_t pdim; - p4est_locidx_t *pcones; - - pdim = *((int8_t *) sc_array_index (node_dim, (size_t) il)); - poff = - dim_cone_offsets[P4EST_DIM - pdim] + 2 * pdim * (pid - - dim_offsets - [P4EST_DIM - - pdim]); - pcones = &cones[poff]; - vid = local_to_plex[cvert + K] - dim_offsets[P4EST_DIM]; - if (pdim == 1) { - p4est_locidx_t pvid[2]; - - pvid[0] = pcones[0] - dim_offsets[P4EST_DIM]; - pvid[1] = pcones[1] - dim_offsets[P4EST_DIM]; - coords[3 * vid + 0] = - 0.5 * (coords[3 * pvid[0] + 0] + coords[3 * pvid[1] + 0]); - coords[3 * vid + 1] = - 0.5 * (coords[3 * pvid[0] + 1] + coords[3 * pvid[1] + 1]); - coords[3 * vid + 2] = - 0.5 * (coords[3 * pvid[0] + 2] + coords[3 * pvid[1] + 2]); - } -#ifdef P4_TO_P8 - else { - int j, k; - - coords[3 * vid + 0] = 0.; - coords[3 * vid + 1] = 0.; - coords[3 * vid + 2] = 0.; - - for (j = 0; j < 4; j++) { - p4est_locidx_t coff, *ccones; - p4est_locidx_t pvid[2]; - - coff = - dim_cone_offsets[P4EST_DIM - 1] + 2 * (pcones[j] - - dim_offsets[P4EST_DIM - - 1]); - ccones = &cones[coff]; - - pvid[0] = ccones[0] - dim_offsets[P4EST_DIM]; - pvid[1] = ccones[1] - dim_offsets[P4EST_DIM]; - for (k = 0; k < 3; k++) { - coords[3 * vid + k] += - 0.125 * (coords[3 * pvid[0] + k] + coords[3 * pvid[1] + k]); - } - } - } -#endif + else { + n_coords = (double *) sc_array_index (node_coords, lid); } - } - if (overlap) { - for (t = 0; t < p4est->connectivity->num_trees; t++) { - p4est_locidx_t il, istart = ghost->tree_offsets[t]; - p4est_locidx_t iend = ghost->tree_offsets[t + 1]; - - for (il = istart; il < iend; il++, qid++) { - p4est_quadrant_t *q = - p4est_quadrant_array_index (&ghost->ghosts, (size_t) il); - p4est_qcoord_t h = P4EST_QUADRANT_LEN (q->level); - int vstart, vend; - - vstart = dim_limits[P4EST_DIM - 1]; - vend = dim_limits[P4EST_DIM]; - for (v = vstart; v < vend; v++) { - int corner = v - vstart; - p4est_locidx_t vid = - local_to_plex[quad_to_local[qid * V + v] + K] - - dim_offsets[P4EST_DIM]; - p4est_locidx_t vid2 = - (quad_to_local_orig != - NULL) ? (local_to_plex[quad_to_local_orig[qid * V + v] + K] - - dim_offsets[P4EST_DIM]) : vid; - double vcoord[3]; - - p4est_qcoord_to_vertex (p4est->connectivity, t, - q->x + ((corner & 1) ? h : 0), - q->y + ((corner & 2) ? h : 0), -#ifdef P4_TO_P8 - q->z + ((corner & 4) ? h : 0), -#endif - vcoord); - coords[3 * vid + 0] = vcoord[0]; - coords[3 * vid + 1] = vcoord[1]; - coords[3 * vid + 2] = vcoord[2]; - - if (vid2 != vid) { - p4est_quadrant_t p; - - p4est_quadrant_parent (q, &p); - p4est_qcoord_to_vertex (p4est->connectivity, t, - p.x + ((corner & 1) ? (2 * h) : 0), - p.y + ((corner & 2) ? (2 * h) : 0), -#ifdef P4_TO_P8 - p.z + ((corner & 4) ? (2 * h) : 0), -#endif - vcoord); - coords[3 * vid2 + 0] = vcoord[0]; - coords[3 * vid2 + 1] = vcoord[1]; - coords[3 * vid2 + 2] = vcoord[2]; - } - } - } + for (int d = 0; d < 3; d++) { + v_coords[d] = n_coords[d]; } } + sc_array_destroy (node_coords); + /* cleanup */ P4EST_FREE (quad_to_local); P4EST_FREE (quad_to_orientations); diff --git a/src/p4est_points.c b/src/p4est_points.c index c45628c6d..d9e57d69a 100644 --- a/src/p4est_points.c +++ b/src/p4est_points.c @@ -181,7 +181,7 @@ p4est_new_points (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, /* allocate memory pools */ p4est->user_data_pool = sc_mempool_new (p4est->data_size); - p4est->quadrant_pool = sc_mempool_new (sizeof (p4est_quadrant_t)); + p4est->quadrant_pool = p4est_quadrant_mempool_new (); P4EST_GLOBAL_PRODUCTIONF ("New " P4EST_STRING " with %lld trees on %d processors\n", @@ -325,8 +325,7 @@ p4est_new_points (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, /* create a complete tree */ if (onlyone) { - quad = p4est_quadrant_array_push (&tree->quadrants); - *quad = a; + quad = p4est_quadrant_array_push_copy (&tree->quadrants, &a); p4est_quadrant_init_data (p4est, jt, quad, p4est_points_init); tree->maxlevel = a.level; ++tree->quadrants_per_level[a.level]; diff --git a/src/p4est_points.h b/src/p4est_points.h index 8f9ff7271..510f50f39 100644 --- a/src/p4est_points.h +++ b/src/p4est_points.h @@ -22,14 +22,6 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/******************************************************************** - * IMPORTANT NOTE * - * * - * The p4est_points functionality depends on sc/src/sc_sort. * - * That parallel bitonic sort is still buggy (see sc/bugs). * - * If you want to use this code you have to fix the sort first. * - ********************************************************************/ - #ifndef P4EST_POINTS_H #define P4EST_POINTS_H diff --git a/src/p4est_search.c b/src/p4est_search.c index dccafe351..06bbcbce9 100644 --- a/src/p4est_search.c +++ b/src/p4est_search.c @@ -33,6 +33,11 @@ #endif #include +#define P4EST_COMM_IS_EMPTY_GFQ_GFP(gfq, gfp, num_procs, p) \ + (((gfq) != NULL) ? \ + p4est_comm_is_empty_gfq ((gfq), (num_procs), (p)) :\ + p4est_comm_is_empty_gfp ((gfp), (num_procs), (p))) + /** A callback function that describes the search window. * The idea is to define the type of an array entry as type 1, if * my_begin <= array[i], my_end > array[i] and as type 2, if the entry @@ -564,12 +569,17 @@ typedef struct p4est_local_recursion p4est_t *p4est; /**< Forest being traversed. */ p4est_topidx_t which_tree; /**< Current tree number. */ int call_post; /**< Boolean to call quadrant twice. */ - p4est_search_local_t quadrant_fn; /**< The quadrant callback. */ - p4est_search_local_t point_fn; /**< The point callback. */ + int skip; /**< Boolean to avoid skipping levels in parallel, if desired*/ + p4est_search_reorder_t children_fn; /**< Reorder children if not NULL. */ + p4est_search_local_t quadrant_fn; /**< The quadrant callback for backwards compatibility, if any. */ + p4est_search_local_t pre_quadrant_fn; /**< The pre recursion quadrant callback, if any. */ + p4est_search_local_t post_quadrant_fn;/**< The post recursion quadrant callback, if any. */ + p4est_search_local_t point_fn; /**< The point callback, if any. */ sc_array_t *points; /**< Array of points to search. */ } p4est_local_recursion_t; +/* The recursion may overwrite the \a quadrant input argument contents. */ static void p4est_local_recursion (const p4est_local_recursion_t * rec, p4est_quadrant_t * quadrant, @@ -591,7 +601,13 @@ p4est_local_recursion (const p4est_local_recursion_t * rec, * 2. quadrant is equal to or an ancestor of those in the array. */ P4EST_ASSERT (rec != NULL); + P4EST_ASSERT (rec->children_fn == NULL); P4EST_ASSERT (quadrant != NULL && quadrants != NULL); + P4EST_ASSERT (quadrants->elem_size == sizeof (p4est_quadrant_t)); + P4EST_ASSERT (rec->skip); /* search_local tries to skip always */ + P4EST_ASSERT (rec->pre_quadrant_fn == NULL); /* unused here */ + P4EST_ASSERT (rec->post_quadrant_fn == NULL); /* unused here */ + qcount = quadrants->elem_count; /* As an optimization we pass a NULL actives array to every root. */ @@ -700,10 +716,10 @@ p4est_local_recursion (const p4est_local_recursion_t * rec, /* split quadrant array and run recursion */ p4est_split_array (quadrants, (int) quadrant->level, split); for (i = 0; i < P4EST_CHILDREN; ++i) { - p4est_quadrant_child (quadrant, &child, i); if (split[i] < split[i + 1]) { sc_array_init_view (&child_quadrants, quadrants, split[i], split[i + 1] - split[i]); + p4est_quadrant_child (quadrant, &child, i); p4est_local_recursion (rec, &child, &child_quadrants, chact); sc_array_reset (&child_quadrants); } @@ -721,7 +737,6 @@ p4est_search_local (p4est_t * p4est, p4est_topidx_t jt; p4est_tree_t *tree; p4est_quadrant_t root; - p4est_quadrant_t *f, *l; p4est_local_recursion_t srec, *rec = &srec; sc_array_t *tquadrants; @@ -738,9 +753,13 @@ p4est_search_local (p4est_t * p4est, rec->p4est = p4est; rec->which_tree = -1; rec->call_post = call_post; + rec->children_fn = NULL; rec->quadrant_fn = quadrant_fn; + rec->pre_quadrant_fn = NULL; + rec->post_quadrant_fn = NULL; rec->point_fn = point_fn; rec->points = points; + rec->skip = 1; for (jt = p4est->first_local_tree; jt <= p4est->last_local_tree; ++jt) { rec->which_tree = jt; @@ -748,16 +767,323 @@ p4est_search_local (p4est_t * p4est, tree = p4est_tree_array_index (p4est->trees, jt); tquadrants = &tree->quadrants; - /* find the smallest quadrant that contains all of this tree */ - f = p4est_quadrant_array_index (tquadrants, 0); - l = p4est_quadrant_array_index (tquadrants, tquadrants->elem_count - 1); - p4est_nearest_common_ancestor (f, l, &root); - - /* perform top-down search */ + /* the recursion shrinks the search quadrant whenever possible */ + p4est_quadrant_set_morton (&root, 0, 0); p4est_local_recursion (rec, &root, tquadrants, NULL); } } +/* The recursion may overwrite the \a quadrant input argument contents. */ +static void +p4est_reorder_recursion (const p4est_local_recursion_t * rec, + p4est_quadrant_t * quadrant, + sc_array_t * quadrants, sc_array_t * actives) +{ + int i; + int is_leaf; + int conchildren; + int level; + int is_same; + size_t qcount, act_count; + size_t zz, *pz, *qz; + size_t split[P4EST_CHILDREN + 1]; + p4est_locidx_t local_num; + p4est_quadrant_t *q; + sc_array_t child_quadrants, child_actives, *chact; + sc_array_t child_indices; + + /* + * Invariants of the recursion: + * 1. quadrant is larger or equal in size than those in the array. + * 2. quadrant is equal to or an ancestor of those in the array. + */ + P4EST_ASSERT (rec != NULL); + P4EST_ASSERT (rec->quadrant_fn == NULL); /* unused here */ + P4EST_ASSERT (quadrant != NULL && quadrants != NULL); + P4EST_ASSERT (quadrants->elem_size == sizeof (p4est_quadrant_t)); + qcount = quadrants->elem_count; + + /* As an optimization we pass a NULL actives array to every root. */ + if (rec->points != NULL && actives == NULL) { + P4EST_ASSERT (quadrant->level == 0); + act_count = rec->points->elem_count; + } + else { + P4EST_ASSERT ((rec->points == NULL) == (actives == NULL)); + P4EST_ASSERT (rec->points == NULL || + actives->elem_count <= rec->points->elem_count); + act_count = actives == NULL ? 0 : actives->elem_count; + } + + /* return if there are no quadrants or active points */ + if (qcount == 0 || (rec->points != NULL && act_count == 0)) { + return; + } + + /* determine leaf situation: can the search quadrant be shrunk? */ + local_num = -1; + is_leaf = is_same = 0; + q = p4est_quadrant_array_index (quadrants, 0); + if (qcount > 1) { + /* There are multiple quadrants in the search window. + This is definitely not a leaf situation. */ + + if (rec->skip) { + p4est_quadrant_t *lq; + + /* access last leaf in the search window in addition to the first */ + P4EST_ASSERT (p4est_quadrant_is_ancestor (quadrant, q)); + lq = p4est_quadrant_array_index (quadrants, quadrants->elem_count - 1); + P4EST_ASSERT (!p4est_quadrant_is_equal (q, lq) && + p4est_quadrant_is_ancestor (quadrant, lq)); + + /* skip unnecessary intermediate levels if possible */ + level = (int) quadrant->level; + if (p4est_quadrant_ancestor_id (q, level + 1) == + p4est_quadrant_ancestor_id (lq, level + 1)) { + p4est_nearest_common_ancestor (q, lq, quadrant); + P4EST_ASSERT (level < (int) quadrant->level); + P4EST_ASSERT (p4est_quadrant_is_ancestor (quadrant, q)); + P4EST_ASSERT (p4est_quadrant_is_ancestor (quadrant, lq)); + } + } + } + else { + P4EST_ASSERT (p4est_quadrant_is_equal (quadrant, q) || + p4est_quadrant_is_ancestor (quadrant, q)); + + /* q is the only leaf in the search quadrant. + It can be reached directly if we skip all intermediate levels. */ + is_leaf = 1; + is_same = p4est_quadrant_is_equal (quadrant, q); + if (rec->skip && !is_same) { + /* we are asked to optimize by skipping intermediate levels */ + is_same = 1; + } + if (is_same) { + p4est_locidx_t offset; + p4est_tree_t *tree; + + /* determine offset of leaf quadrant in local forest */ + tree = p4est_tree_array_index (rec->p4est->trees, rec->which_tree); + offset = (p4est_locidx_t) ((quadrants->array - tree->quadrants.array) + / sizeof (p4est_quadrant_t)); + P4EST_ASSERT (offset >= 0 && + (size_t) offset < tree->quadrants.elem_count); + local_num = tree->quadrants_offset + offset; + + /* make sure we pass the leaf itself to subsequent callbacks */ + quadrant = q; + } + } + P4EST_ASSERT (!is_same || is_leaf); + P4EST_ASSERT (!is_same == !p4est_quadrant_is_equal (quadrant, q)); + P4EST_ASSERT (is_same || quadrant->level < P4EST_QMAXLEVEL); + + /* execute pre-quadrant callback if present, which may stop the recursion */ + if (rec->pre_quadrant_fn != NULL && !rec->pre_quadrant_fn + (rec->p4est, rec->which_tree, quadrant, local_num, NULL)) { + return; + } + + /* call point callback on remaining points, skip to post if none remain */ + chact = NULL; + conchildren = 1; + if (rec->points != NULL) { + /* query callback for all points and skip to post if none remain */ + chact = &child_actives; + sc_array_init (chact, sizeof (size_t)); + for (zz = 0; zz < act_count; ++zz) { + pz = actives == NULL ? &zz : (size_t *) sc_array_index (actives, zz); + if (rec->point_fn (rec->p4est, rec->which_tree, quadrant, local_num, + sc_array_index (rec->points, *pz)) && !is_same) { + qz = (size_t *) sc_array_push (chact); + *qz = *pz; + } + } + if (chact->elem_count == 0) { + /* without members (forced for any leaf) no need to call sc_array_reset */ + chact = NULL; + conchildren = 0; + } + } + + /* execute search recursion, either when points remain or there never were any */ + if (conchildren && !is_leaf) { + /* identify search children, figure out which are relevant */ + P4EST_ASSERT (qcount > 1); + p4est_split_array (quadrants, (int) quadrant->level, split); + sc_array_init (&child_indices, sizeof (p4est_topidx_t)); + for (i = 0; i < P4EST_CHILDREN; ++i) { + if (split[i] < split[i + 1]) { + *(p4est_topidx_t *) sc_array_push (&child_indices) = i; + } + } + P4EST_ASSERT (child_indices.elem_count > 0); + + /* reorder/reduce search children, skip to post if callback returns false */ + if (rec->children_fn != NULL) { + sc_array_t childrenview; + p4est_quadrant_t children[P4EST_CHILDREN]; + p4est_quadrant_childrenv (quadrant, children); + for (i = 0; i < P4EST_CHILDREN; ++i) { + children[i].p.piggy1.which_tree = rec->which_tree; + } + sc_array_init_data (&childrenview, children, + sizeof (p4est_quadrant_t), P4EST_CHILDREN); + conchildren = + rec->children_fn (rec->p4est, &childrenview, &child_indices); + } + P4EST_ASSERT (child_indices.elem_count <= P4EST_CHILDREN); + + /* go into recursion in potentially reordered child order */ + if (conchildren) { + p4est_quadrant_t child; + + for (zz = 0; zz < child_indices.elem_count; ++zz) { + i = (int) *(p4est_topidx_t *) sc_array_index (&child_indices, zz); + P4EST_ASSERT (0 <= i && i < P4EST_CHILDREN); + P4EST_ASSERT (split[i] < split[i + 1]); + + sc_array_init_view (&child_quadrants, quadrants, + split[i], split[i + 1] - split[i]); + p4est_quadrant_child (quadrant, &child, i); + p4est_reorder_recursion (rec, &child, &child_quadrants, chact); + sc_array_reset (&child_quadrants); + } + } + sc_array_reset (&child_indices); + } + + /* follow down single quadrant, either when points remain or there never were any */ + if (conchildren && is_leaf && !is_same) { + int level_diff; + p4est_quadrant_t child; + + P4EST_ASSERT (!rec->skip); + P4EST_ASSERT (qcount == 1); + level_diff = q->level - quadrant->level; + P4EST_ASSERT (level_diff > 0); + + /* construct the search window one level finer than current quadrant */ + if (level_diff > 1) { + p4est_quadrant_ancestor (q, quadrant->level + 1, &child); + P4EST_ASSERT (quadrant->level < child.level && child.level < q->level); + } + else { + P4EST_ASSERT (level_diff == 1); + child = *q; /* child is not part of the actual forest storage */ + } + P4EST_ASSERT (p4est_quadrant_is_parent (quadrant, &child)); + p4est_reorder_recursion (rec, &child, quadrants, chact); + } + + /* free temporary point storage for children */ + if (chact != NULL) { + sc_array_reset (chact); + } + + /* always call post callback on search quadrant, ignoring its return value. */ + if (rec->post_quadrant_fn != NULL) { + rec->post_quadrant_fn (rec->p4est, rec->which_tree, + quadrant, local_num, NULL); + } +} + +void +p4est_search_reorder (p4est_t * p4est, int skip_levels, + p4est_search_reorder_t reorder_fn, + p4est_search_query_t pre_quadrant_fn, + p4est_search_query_t post_quadrant_fn, + p4est_search_query_t point_fn, sc_array_t * points) +{ + sc_array_t *tquadrants; + sc_array_t *root_indices; + p4est_topidx_t num_local_trees, tt, rit; + p4est_tree_t *tree; + p4est_quadrant_t root; + p4est_local_recursion_t srec, *rec = &srec; + + /* check call convention */ + P4EST_ASSERT (p4est != NULL); + P4EST_ASSERT (points == NULL || point_fn != NULL); + + /* we do nothing if there is nothing we can do */ + if (pre_quadrant_fn == NULL && post_quadrant_fn == NULL && point_fn == NULL) { + return; + } + + /* do nothing if there are no local quadrants */ + if (p4est->first_local_tree > p4est->last_local_tree) { + return; + } + num_local_trees = p4est->last_local_tree - p4est->first_local_tree + 1; + P4EST_ASSERT (num_local_trees > 0); + + /* reorder trees if so desired */ + root_indices = NULL; + if (reorder_fn != NULL) { + p4est_quadrant_t *proot; + int contrees; + + tquadrants = + sc_array_new_count (sizeof (p4est_quadrant_t), num_local_trees); + root_indices = + sc_array_new_count (sizeof (p4est_topidx_t), num_local_trees); + for (tt = p4est->first_local_tree; tt <= p4est->last_local_tree; ++tt) { + rit = tt - p4est->first_local_tree; + proot = (p4est_quadrant_t *) sc_array_index (tquadrants, rit); + p4est_quadrant_set_morton (proot, 0, 0); + proot->p.piggy1.which_tree = tt; + *(p4est_topidx_t *) sc_array_index (root_indices, rit) = rit; + } + + /* invoke reorder callback */ + contrees = reorder_fn (p4est, tquadrants, root_indices); + sc_array_destroy (tquadrants); + if (!contrees) { + /* returning false breaks recursion */ + sc_array_destroy (root_indices); + return; + } + } + + /* set recursion context */ + rec->p4est = p4est; + rec->which_tree = -1; + rec->call_post = 1; + rec->children_fn = reorder_fn; + rec->quadrant_fn = NULL; + rec->pre_quadrant_fn = pre_quadrant_fn; + rec->post_quadrant_fn = post_quadrant_fn; + rec->point_fn = point_fn; + rec->points = points; + rec->skip = skip_levels; + + /* go through tree recursions in proper order */ + for (tt = p4est->first_local_tree; tt <= p4est->last_local_tree; ++tt) { + rec->which_tree = root_indices != NULL ? *(p4est_topidx_t *) + sc_array_index (root_indices, tt - p4est->first_local_tree) + + p4est->first_local_tree : tt; + P4EST_ASSERT (p4est->first_local_tree <= rec->which_tree && + rec->which_tree <= p4est->last_local_tree); + + /* grab complete tree quadrant array */ + tree = p4est_tree_array_index (p4est->trees, rec->which_tree); + tquadrants = &tree->quadrants; + + /* the recursion shrinks the search quadrant whenever possible */ + p4est_quadrant_set_morton (&root, 0, 0); + p4est_reorder_recursion (rec, &root, tquadrants, NULL); + } + + /* cleanup sorted tree indices */ + P4EST_ASSERT ((reorder_fn == NULL) == (root_indices == NULL)); + if (reorder_fn != NULL) { + sc_array_destroy (root_indices); + } +} + void p4est_search (p4est_t * p4est, p4est_search_query_t quadrant_fn, p4est_search_query_t point_fn, sc_array_t * points) @@ -795,19 +1121,20 @@ p4est_traverse_type_tree (sc_array_t * array, size_t pindex, void *data) * \return True if and only if \b p starts with \p quadrant. */ static int -p4est_traverse_is_clean_start (p4est_t * p4est, - p4est_quadrant_t * quadrant, int p) +p4est_traverse_is_clean_start (const p4est_quadrant_t *gfp, int num_procs, + p4est_topidx_t num_trees, + p4est_quadrant_t *quadrant, int p) { const p4est_quadrant_t *marker; - P4EST_ASSERT (p4est != NULL); + P4EST_ASSERT (gfp != NULL); P4EST_ASSERT (quadrant != NULL); - P4EST_ASSERT (0 <= p && p <= p4est->mpisize); + P4EST_ASSERT (0 <= p && p <= num_procs); - marker = p4est->global_first_position + p; + marker = gfp + p; P4EST_ASSERT (marker->level == P4EST_QMAXLEVEL); P4EST_ASSERT (0 <= marker->p.which_tree && - marker->p.which_tree <= p4est->connectivity->num_trees); + marker->p.which_tree <= num_trees); P4EST_ASSERT (marker->p.which_tree == quadrant->p.which_tree); return marker->x == quadrant->x && marker->y == quadrant->y @@ -836,53 +1163,56 @@ p4est_traverse_type_childid (sc_array_t * array, size_t pindex, void *data) #ifdef P4EST_ENABLE_DEBUG -static int -p4est_traverse_is_valid_quadrant (p4est_t * p4est, p4est_topidx_t which_tree, - const p4est_quadrant_t * quadrant, - int pfirst, int plast) +static int p4est_traverse_is_valid_quadrant + (const p4est_quadrant_t *gfp, int num_procs, p4est_topidx_t num_trees, + p4est_topidx_t which_tree, const p4est_quadrant_t *quadrant, + int pfirst, int plast) { p4est_quadrant_t desc; - P4EST_ASSERT (p4est != NULL); - P4EST_ASSERT (0 <= which_tree && - which_tree < p4est->connectivity->num_trees); + P4EST_ASSERT (gfp != NULL); + P4EST_ASSERT (0 <= which_tree && which_tree < num_trees); P4EST_ASSERT (p4est_quadrant_is_valid (quadrant)); - P4EST_ASSERT (0 <= pfirst && pfirst <= plast && plast < p4est->mpisize); + P4EST_ASSERT (0 <= pfirst && pfirst <= plast && plast < num_procs); /* check that pfirst is really the first owner in this quadrant */ p4est_quadrant_first_descendant (quadrant, &desc, P4EST_QMAXLEVEL); - if (!p4est_comm_is_owner (p4est, which_tree, &desc, pfirst)) { + if (!p4est_comm_is_owner_gfp (gfp, num_procs, num_trees, + which_tree, &desc, pfirst)) { return 0; } /* check that plast is really the last owner in this quadrant */ p4est_quadrant_last_descendant (quadrant, &desc, P4EST_QMAXLEVEL); - if (!p4est_comm_is_owner (p4est, which_tree, &desc, plast)) { + if (!p4est_comm_is_owner_gfp (gfp, num_procs, num_trees, + which_tree, &desc, plast)) { return 0; } +#if 0 /* this is redundant after the checks above */ - P4EST_ASSERT (!p4est_comm_is_empty (p4est, pfirst) && - !p4est_comm_is_empty (p4est, plast)); + /* we're optimizing this away to avoid passing gfq in addition */ + P4EST_ASSERT (!p4est_comm_is_empty_gfq (gfq, num_procs, pfirst) && + !p4est_comm_is_empty_gfq (gfq, num_procs, plast)); +#endif return 1; } -static int -p4est_traverse_is_valid_tree (p4est_t * p4est, p4est_topidx_t which_tree, - int pfirst, int plast) +static int p4est_traverse_is_valid_tree + (const p4est_quadrant_t *gfp, int num_procs, p4est_topidx_t num_trees, + p4est_topidx_t which_tree, int pfirst, int plast) { p4est_quadrant_t root; - P4EST_ASSERT (p4est != NULL); - P4EST_ASSERT (0 <= which_tree && - which_tree < p4est->connectivity->num_trees); - P4EST_ASSERT (0 <= pfirst && pfirst <= plast && plast < p4est->mpisize); + P4EST_ASSERT (gfp != NULL); + P4EST_ASSERT (0 <= which_tree && which_tree < num_trees); + P4EST_ASSERT (0 <= pfirst && pfirst <= plast && plast < num_procs); p4est_quadrant_set_morton (&root, 0, 0); - return p4est_traverse_is_valid_quadrant (p4est, which_tree, &root, - pfirst, plast); + return p4est_traverse_is_valid_quadrant + (gfp, num_procs, num_trees, which_tree, &root, pfirst, plast); } #endif /* P4EST_ENABLE_DEBUG */ @@ -890,13 +1220,17 @@ p4est_traverse_is_valid_tree (p4est_t * p4est, p4est_topidx_t which_tree, /** This recursion context saves on the number of parameters passed. */ typedef struct p4est_partition_recursion { - p4est_t *p4est; /**< Forest being traversed. */ + p4est_t *user_p4est; /**< Forest passed to callbacks. */ + const p4est_gloidx_t *gfq; /**< Global first quadrant array. */ + const p4est_quadrant_t *gfp; /**< Global first positiion array. */ + int num_procs; /**< Number of ranks in partition. */ + p4est_topidx_t num_trees; /**< Number of trees in partition. */ p4est_topidx_t which_tree; /**< Current tree number. */ int call_post; /**< Boolean to call quadrant twice. */ p4est_search_partition_t quadrant_fn; /**< Per-quadrant callback. */ p4est_search_partition_t point_fn; /**< Per-point callback. */ sc_array_t *points; /**< Array of points to search. */ - sc_array_t *position_array; /**< Array view of p4est's + sc_array_t *position_array; /**< Array view of partition's global_first_position */ } p4est_partition_recursion_t; @@ -916,10 +1250,10 @@ p4est_partition_recursion (const p4est_partition_recursion_t * rec, sc_array_t child_actives, *chact; P4EST_ASSERT (rec != NULL); - P4EST_ASSERT (0 <= rec->which_tree && - rec->which_tree < rec->p4est->connectivity->num_trees); - P4EST_ASSERT (p4est_traverse_is_valid_quadrant (rec->p4est, rec->which_tree, - quadrant, pfirst, plast)); + P4EST_ASSERT (0 <= rec->which_tree && rec->which_tree < rec->num_trees); + P4EST_ASSERT (p4est_traverse_is_valid_quadrant + (rec->gfp, rec->num_procs, rec->num_trees, + rec->which_tree, quadrant, pfirst, plast)); P4EST_ASSERT (quadrant != NULL && p4est_quadrant_is_valid (quadrant)); P4EST_ASSERT (quadrant->p.which_tree == rec->which_tree); @@ -940,7 +1274,7 @@ p4est_partition_recursion (const p4est_partition_recursion_t * rec, /* execute pre-quadrant callback if present, which may stop the recursion */ if (rec->quadrant_fn != NULL && - !rec->quadrant_fn (rec->p4est, rec->which_tree, + !rec->quadrant_fn (rec->user_p4est, rec->which_tree, quadrant, pfirst, plast, NULL)) { return; } @@ -959,7 +1293,7 @@ p4est_partition_recursion (const p4est_partition_recursion_t * rec, sc_array_init (chact, sizeof (size_t)); for (zz = 0; zz < act_count; ++zz) { pz = actives == NULL ? &zz : (size_t *) sc_array_index (actives, zz); - is_match = rec->point_fn (rec->p4est, rec->which_tree, + is_match = rec->point_fn (rec->user_p4est, rec->which_tree, quadrant, pfirst, plast, sc_array_index (rec->points, *pz)); if (!(pfirst == plast) && is_match) { @@ -970,7 +1304,7 @@ p4est_partition_recursion (const p4est_partition_recursion_t * rec, /* call post-quadrant callback, which may also terminate the recursion */ if (rec->call_post && rec->quadrant_fn != NULL && - !rec->quadrant_fn (rec->p4est, rec->which_tree, + !rec->quadrant_fn (rec->user_p4est, rec->which_tree, quadrant, pfirst, plast, NULL)) { /* clears memory and will trigger the return below */ sc_array_reset (chact); @@ -1014,10 +1348,12 @@ p4est_partition_recursion (const p4est_partition_recursion_t * rec, if (cpfirst < cpnext) { /* at least one processor starts in this child */ - if (p4est_traverse_is_clean_start (rec->p4est, &child, cpfirst)) { + if (p4est_traverse_is_clean_start + (rec->gfp, rec->num_procs, rec->num_trees, &child, cpfirst)) { /* cpfirst starts at the tree's first descendant but may be empty */ P4EST_ASSERT (i > 0); - while (p4est_comm_is_empty (rec->p4est, cpfirst)) { + while (P4EST_COMM_IS_EMPTY_GFQ_GFP + (rec->gfq, rec->gfp, rec->num_procs, cpfirst)) { ++cpfirst; P4EST_ASSERT (p4est_traverse_type_childid (rec->position_array, cpfirst, quadrant) == @@ -1041,12 +1377,21 @@ p4est_partition_recursion (const p4est_partition_recursion_t * rec, P4EST_ASSERT (i > 0 || pfirst == cpfirst); P4EST_ASSERT (i < P4EST_CHILDREN - 1 || plast == cplast); P4EST_ASSERT (pfirst <= cpfirst && cpfirst <= cplast && cplast <= plast); - P4EST_ASSERT (cplast <= cpnext && cpnext <= plast + 1); + + /* we know these are non-negative; check before casting to unsigned */ + P4EST_ASSERT (cplast >= 0 && cpnext >= 0 && plast + 1 >= 0); + + /* These casts remove compiler warnings due to the assumption of the + * compiler under -O3 that there cannot happen a signed overflow. + */ + P4EST_ASSERT ((unsigned) cplast <= (unsigned) cpnext + && (unsigned) cpnext <= (unsigned) plast + 1); P4EST_ASSERT (cplast == pfirst || p4est_traverse_type_childid (rec->position_array, cplast, quadrant) <= (size_t) i); P4EST_ASSERT (p4est_traverse_is_valid_quadrant - (rec->p4est, rec->which_tree, &child, cpfirst, cplast)); + (rec->gfp, rec->num_procs, rec->num_trees, + rec->which_tree, &child, cpfirst, cplast)); /* go deeper into the recursion */ p4est_partition_recursion (rec, &child, cpfirst, cplast, chact); @@ -1060,14 +1405,95 @@ p4est_partition_recursion (const p4est_partition_recursion_t * rec, sc_array_reset (&pview); } +static void p4est_search_partition_internal + (const p4est_gloidx_t *gfq, const p4est_quadrant_t *gfp, + int nmemb, p4est_topidx_t num_trees, int call_post, p4est_t *user_p4est, + p4est_search_partition_t quadrant_fn, p4est_search_partition_t point_fn, + sc_array_t *points); + void -p4est_search_partition (p4est_t * p4est, - int call_post, p4est_search_partition_t quadrant_fn, +p4est_search_partition (p4est_t *p4est, int call_post, + p4est_search_partition_t quadrant_fn, p4est_search_partition_t point_fn, - sc_array_t * points) + sc_array_t *points) { - const int num_procs = p4est->mpisize; - const p4est_topidx_t num_trees = p4est->connectivity->num_trees; + P4EST_ASSERT (p4est != NULL); + P4EST_ASSERT (p4est->connectivity != NULL); + + p4est_search_partition_internal + (p4est->global_first_quadrant, p4est->global_first_position, + p4est->mpisize, p4est->connectivity->num_trees, + call_post, p4est, quadrant_fn, point_fn, points); +} + +void +p4est_search_partition_gfx (const p4est_gloidx_t *gfq, + const p4est_quadrant_t *gfp, + int nmemb, p4est_topidx_t num_trees, + int call_post, void *user, + p4est_search_partition_t quadrant_fn, + p4est_search_partition_t point_fn, + sc_array_t *points) +{ + p4est_t p, *user_p4est = &p; + + /* sanity checks on global first quadrant */ + P4EST_ASSERT (gfq != NULL); + P4EST_ASSERT (gfq[0] == 0); + P4EST_ASSERT (gfq[nmemb] >= (p4est_gloidx_t) num_trees); + + /* sanity checks on global first position */ + P4EST_ASSERT (gfp != NULL); + P4EST_ASSERT (gfp[0].p.which_tree == 0); + P4EST_ASSERT (gfp[nmemb].x == 0); + P4EST_ASSERT (gfp[nmemb].y == 0); +#ifdef P4_TO_P8 + P4EST_ASSERT (gfp[nmemb].z == 0); +#endif + P4EST_ASSERT (gfp[nmemb].p.which_tree == num_trees); + + /* conjure up call convention for partition search */ + memset (user_p4est, 0, sizeof (p4est_t)); + user_p4est->user_pointer = user; + p4est_search_partition_internal + (gfq, gfp, nmemb, num_trees, call_post, user_p4est, + quadrant_fn, point_fn, points); +} + +void +p4est_search_partition_gfp (const p4est_quadrant_t *gfp, int nmemb, + p4est_topidx_t num_trees, int call_post, + void *user, p4est_search_partition_t quadrant_fn, + p4est_search_partition_t point_fn, + sc_array_t *points) +{ + p4est_t p, *user_p4est = &p; + + /* sanity checks on global first position */ + P4EST_ASSERT (gfp != NULL); + P4EST_ASSERT (gfp[0].p.which_tree == 0); + P4EST_ASSERT (gfp[nmemb].x == 0); + P4EST_ASSERT (gfp[nmemb].y == 0); +#ifdef P4_TO_P8 + P4EST_ASSERT (gfp[nmemb].z == 0); +#endif + P4EST_ASSERT (gfp[nmemb].p.which_tree == num_trees); + + /* conjure up call convention for partition search */ + memset (user_p4est, 0, sizeof (p4est_t)); + user_p4est->user_pointer = user; + p4est_search_partition_internal + (NULL, gfp, nmemb, num_trees, call_post, user_p4est, + quadrant_fn, point_fn, points); +} + +void p4est_search_partition_internal + (const p4est_gloidx_t *gfq, const p4est_quadrant_t *gfp, + int nmemb, p4est_topidx_t num_trees, int call_post, p4est_t *user_p4est, + p4est_search_partition_t quadrant_fn, p4est_search_partition_t point_fn, + sc_array_t *points) +{ + const int num_procs = nmemb; int pfirst, plast, pnext; sc_array_t position_array; sc_array_t *tree_offsets; @@ -1076,15 +1502,15 @@ p4est_search_partition (p4est_t * p4est, p4est_partition_recursion_t srec, *rec = &srec; /* we do nothing if there is nothing to be done */ - P4EST_ASSERT (p4est != NULL); + P4EST_ASSERT (gfp != NULL); P4EST_ASSERT (points == NULL || point_fn != NULL); if (quadrant_fn == NULL && points == NULL) { return; } - /* array to split is the p4est partition marker */ + /* array to split is the p4est partition marker. A const crime */ /* it is important to include the highest tree number plus one */ - sc_array_init_data (&position_array, p4est->global_first_position, + sc_array_init_data (&position_array, (p4est_quadrant_t *) gfp, sizeof (p4est_quadrant_t), num_procs + 1); /* the enumerable type is the tree number -- we know the size already */ @@ -1101,7 +1527,11 @@ p4est_search_partition (p4est_t * p4est, P4EST_ASSERT (p4est_traverse_array_index (tree_offsets, 0) == 0); /* now loop through all trees, local or not */ - rec->p4est = p4est; + rec->user_p4est = user_p4est; + rec->gfq = gfq; + rec->gfp = gfp; + rec->num_procs = num_procs; + rec->num_trees = num_trees; rec->which_tree = -1; rec->call_post = call_post; rec->quadrant_fn = quadrant_fn; @@ -1125,9 +1555,11 @@ p4est_search_partition (p4est_t * p4est, if (pfirst < pnext) { /* at least one processor starts in this tree */ - if (p4est_traverse_is_clean_start (p4est, &root, pfirst)) { + if (p4est_traverse_is_clean_start + (rec->gfp, rec->num_procs, rec->num_trees, &root, pfirst)) { /* pfirst starts at the tree's first descendant but may be empty */ - while (p4est_comm_is_empty (p4est, pfirst)) { + while (P4EST_COMM_IS_EMPTY_GFQ_GFP + (rec->gfq, rec->gfp, rec->num_procs, pfirst)) { ++pfirst; P4EST_ASSERT (p4est_traverse_type_tree (&position_array, pfirst, NULL) == (size_t) tt); @@ -1147,10 +1579,20 @@ p4est_search_partition (p4est_t * p4est, /* we should have found tight bounds on processors for this tree */ P4EST_ASSERT (pfirst <= plast && plast < num_procs); - P4EST_ASSERT (plast <= pnext && pnext <= num_procs); + + /* we know these are non-negative; check before casting to unsigned */ + P4EST_ASSERT (plast >= 0 && pnext >= 0 && num_procs >= 0); + + /* These casts remove compiler warnings due to the assumption of the + * compiler under -O3 that there cannot happen a signed overflow. + */ + P4EST_ASSERT ((unsigned) plast <= (unsigned) pnext + && (unsigned) pnext <= (unsigned) num_procs); P4EST_ASSERT (p4est_traverse_type_tree (&position_array, plast, NULL) <= (size_t) tt); - P4EST_ASSERT (p4est_traverse_is_valid_tree (p4est, tt, pfirst, plast)); + P4EST_ASSERT (p4est_traverse_is_valid_tree + (rec->gfp, rec->num_procs, rec->num_trees, + tt, pfirst, plast)); /* go into recursion for this tree */ p4est_partition_recursion (rec, &root, pfirst, plast, NULL); @@ -1165,6 +1607,10 @@ p4est_search_partition (p4est_t * p4est, typedef struct p4est_all_recursion { p4est_t *p4est; /**< Forest being traversed. */ + const p4est_gloidx_t *gfq; /**< Global first quadrant array. */ + const p4est_quadrant_t *gfp; /**< Global first positiion array. */ + int num_procs; /**< Number of ranks in partition. */ + p4est_topidx_t num_trees; /**< Number of trees in partition. */ p4est_topidx_t which_tree; /**< Current tree number. */ int call_post; /**< Boolean to call quadrant twice. */ p4est_search_all_t quadrant_fn; /**< Per-quadrant callback. */ @@ -1195,8 +1641,9 @@ p4est_all_recursion (const p4est_all_recursion_t * rec, P4EST_ASSERT (rec != NULL); P4EST_ASSERT (0 <= rec->which_tree && rec->which_tree < rec->p4est->connectivity->num_trees); - P4EST_ASSERT (p4est_traverse_is_valid_quadrant (rec->p4est, rec->which_tree, - quadrant, pfirst, plast)); + P4EST_ASSERT (p4est_traverse_is_valid_quadrant + (rec->gfp, rec->num_procs, rec->num_trees, + rec->which_tree, quadrant, pfirst, plast)); P4EST_ASSERT (quadrant != NULL && p4est_quadrant_is_valid (quadrant)); P4EST_ASSERT (quadrant->p.which_tree == rec->which_tree); @@ -1305,7 +1752,7 @@ p4est_all_recursion (const p4est_all_recursion_t * rec, } } - /* the stoping condition (including we being a leaf) has returned above */ + /* the stopping condition (including we being a leaf) has returned above */ P4EST_ASSERT (proceed); P4EST_ASSERT (quadrant->level < P4EST_QMAXLEVEL); @@ -1342,7 +1789,8 @@ p4est_all_recursion (const p4est_all_recursion_t * rec, if (cpfirst < cpnext) { /* at least one processor starts in this child */ - if (p4est_traverse_is_clean_start (rec->p4est, &child, cpfirst)) { + if (p4est_traverse_is_clean_start + (rec->gfp, rec->num_procs, rec->num_trees, &child, cpfirst)) { /* cpfirst starts at the tree's first descendant but may be empty */ P4EST_ASSERT (i > 0); while (p4est_comm_is_empty (rec->p4est, cpfirst)) { @@ -1369,12 +1817,21 @@ p4est_all_recursion (const p4est_all_recursion_t * rec, P4EST_ASSERT (i > 0 || pfirst == cpfirst); P4EST_ASSERT (i < P4EST_CHILDREN - 1 || plast == cplast); P4EST_ASSERT (pfirst <= cpfirst && cpfirst <= cplast && cplast <= plast); - P4EST_ASSERT (cplast <= cpnext && cpnext <= plast + 1); + + /* we know these are non-negative; check before casting to unsigned */ + P4EST_ASSERT (cplast >= 0 && cpnext >= 0 && plast + 1 >= 0); + + /* These casts remove compiler warnings due to the assumption of the + * compiler under -O3 that there cannot happen a signed overflow. + */ + P4EST_ASSERT ((unsigned) cplast <= (unsigned) cpnext + && (unsigned) cpnext <= (unsigned) plast + 1); P4EST_ASSERT (cplast == pfirst || p4est_traverse_type_childid (rec->position_array, cplast, quadrant) <= (size_t) i); P4EST_ASSERT (p4est_traverse_is_valid_quadrant - (rec->p4est, rec->which_tree, &child, cpfirst, cplast)); + (rec->gfp, rec->num_procs, rec->num_trees, + rec->which_tree, &child, cpfirst, cplast)); /* designate the subarray of local quadrants */ chpass = NULL; @@ -1442,6 +1899,10 @@ p4est_search_all (p4est_t * p4est, /* now loop through all trees, local or not */ rec->p4est = p4est; + rec->gfq = p4est->global_first_quadrant; + rec->gfp = p4est->global_first_position; + rec->num_procs = num_procs; + rec->num_trees = num_trees; rec->which_tree = -1; rec->call_post = call_post; rec->quadrant_fn = quadrant_fn; @@ -1465,7 +1926,8 @@ p4est_search_all (p4est_t * p4est, if (pfirst < pnext) { /* at least one processor starts in this tree */ - if (p4est_traverse_is_clean_start (p4est, &root, pfirst)) { + if (p4est_traverse_is_clean_start + (rec->gfp, rec->num_procs, rec->num_trees, &root, pfirst)) { /* pfirst starts at the tree's first descendant but may be empty */ while (p4est_comm_is_empty (p4est, pfirst)) { ++pfirst; @@ -1487,10 +1949,20 @@ p4est_search_all (p4est_t * p4est, /* we should have found tight bounds on processors for this tree */ P4EST_ASSERT (pfirst <= plast && plast < num_procs); - P4EST_ASSERT (plast <= pnext && pnext <= num_procs); + + /* we know these are non-negative; check before casting to unsigned */ + P4EST_ASSERT (plast >= 0 && pnext >= 0 && num_procs >= 0); + + /* These casts remove compiler warnings due to the assumption of the + * compiler under -O3 that there cannot happen a signed overflow. + */ + P4EST_ASSERT ((unsigned) plast <= (unsigned) pnext + && (unsigned) pnext <= (unsigned) num_procs); P4EST_ASSERT (p4est_traverse_type_tree (&position_array, plast, NULL) <= (size_t) tt); - P4EST_ASSERT (p4est_traverse_is_valid_tree (p4est, tt, pfirst, plast)); + P4EST_ASSERT (p4est_traverse_is_valid_tree + (rec->gfp, rec->num_procs, rec->num_trees, + tt, pfirst, plast)); /* if this tree is at least partially local, get the local quadrants */ if (p4est->first_local_tree <= tt && tt <= p4est->last_local_tree) { diff --git a/src/p4est_search.h b/src/p4est_search.h index b948a0c4d..a68101a86 100644 --- a/src/p4est_search.h +++ b/src/p4est_search.h @@ -75,9 +75,8 @@ SC_EXTERN_C_BEGIN; * \param [in] num_procs Number of processes to get the length of * \a search_in. * \param [in] search_in The sorted array (ascending) in that the function - * will search. - * If `k` indexes search_in, then - * `0 <= k < num_procs`. + * will search. If `k` indexes search_in, then `0 <= + * k < num_procs`. * \param [in] my_begin The first target that defines the start of the * search window. * \param [in] my_end The second target that defines the end of the @@ -116,7 +115,7 @@ ssize_t p4est_find_higher_bound (sc_array_t * array, * which means that it is advisable NOT to use this function if possible, * and to try to maintain O(1) tree context information in the calling code. * - * \param [in] p4est Forest to be worked with. + * \param [in] p4est Forest to work with. * \param [in] cumulative_id Cumulative index over all trees of quadrant. * \param [in,out] which_tree If not NULL, the input value can be -1 * or an initial guess for the quadrant's tree. @@ -254,7 +253,8 @@ typedef p4est_search_local_t p4est_search_query_t; * empty array, the recursion will stop immediately! * * \param [in] p4est The forest to be searched. - * \param [in] call_post If true, call quadrant callback both pre and post. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). * \param [in] quadrant_fn Executed once when a quadrant is entered, and once * when it is left (the second time only if points are * present and the first call returned true). @@ -286,6 +286,81 @@ void p4est_search (p4est_t * p4est, p4est_search_query_t point_fn, sc_array_t * points); +/** Callback function to query, reorder, and reduce a set of quadrants. + * It receives an array of quadrants and an array of array indices on input. + * On output, the array of quadrants is unmodified but the indices may be. + * This function may permute the indices and/or choose a subset. + * Subsetting is effected by resizing the index array. Note to resize only + * before or after, but not during eventual sorting, since resizing may + * reallocate and thus move the array memory. + * Indices must remain a permutation. + * \param [in] p4est The forest to be queried. + * \param [in] quadrants The quadrant array under consideration, + * each with valid coordinates and level. + * The user data piggy1 field of each quadrant + * contains its tree number. Sorted ascending. + * \param [in,out] indices This array holds \ref p4est_topidx_t types. + * Sorted ascending on input. May be permuted and + * subset by this function. It is explicitly allowed + * to \ref sc_array_resize to smaller length. + * An output length of zero stops recursion. + * \return Return false to break the search recursion. + */ +typedef int (*p4est_search_reorder_t) (p4est_t * p4est, + sc_array_t * quadrants, + sc_array_t * indices); + +/** Run a depth-first traversal, optionally filtering search points. + * There are three main differences to \ref p4est_search_local : + * + * * Before beginning the recursion, we call the \a reorder_fn callback + * with an index array enumerating the local tree roots. The callback + * may permute its entries to define the order of trees to traverse. + * * After the pre-quadrant callback and its point callbacks, the \a + * reorder callback is passed an index array to relevant child numbers + * of the branch quadrant, ordered but possibly non-contiguous. It may + * permute these to indicate the sequence of the children to traverse. + * * The post-quadrant callback is executed after the recursion returns. + * Even for leaves, it is called whenever the pre-callback returned true. + * Even called when all points have been unmatched by the point callback. + * + * \param [in] p4est The forest to be searched. + * \param [in] skip_levels If true and there is a search window that + * contains a single descendant, or if all quadrants + * in the search window are descendants of one child + * of it, skip the intermediate recursion levels. + * \param [in] reorder_fn Called with \a quadrants input array containing + * either the local tree roots or a set of siblings. + * The array may be permuted on output to define the + * order of traversal of the quadrants. + * May be NULL to omit reordering, always recurse. + * If not NULL and it returns true, don't recurse. + * \param [in] pre_quadrant_fn As in \ref p4est_search_local, pre-order callback. + * If the pre-callback returns false, recursion stops. + * If it returns true, recursion continues. + * The \a quadrant argument is the same pre and post. + * \param [in] post_quadrant_fn As in \ref p4est_search_local, post-order callback. + * It is called whenever the pre-callback returns true. + * The \a quadrant argument is the same pre and post. + * \param [in] point_fn As in \ref p4est_search_local. + * \param [in,out] points As in \ref p4est_search_local. + * May be NULL to use quadrant callbacks only. + * Otherwise, if no points remain for a + * particular search quadrant, the recursion + * stops even if the quadrant callback indicates + * to continue. This behavior can be prevented + * by always keeping one bogus point around. + */ +void p4est_search_reorder (p4est_t * p4est, + int skip_levels, + p4est_search_reorder_t reorder_fn, + p4est_search_local_t + pre_quadrant_fn, + p4est_search_local_t + post_quadrant_fn, + p4est_search_local_t point_fn, + sc_array_t * points); + /** Callback function for the partition recursion. * \param [in] p4est The forest to traverse. * Its local quadrants are never accessed. @@ -311,6 +386,7 @@ typedef int (*p4est_search_partition_t) (p4est_t * p4est, void *point); /** Traverse the global partition top-down. + * This is not a collective function. It does not communicate. * We proceed top-down through the partition, identically on all processors * except for the results of two user-provided callbacks. The recursion will only * go down branches that are split between multiple processors. The callback @@ -321,7 +397,8 @@ typedef int (*p4est_search_partition_t) (p4est_t * p4est, * so sensible use of the callback function is advised to cut it short. * \param [in] p4est The forest to traverse. * Its local quadrants are never accessed. - * \param [in] call_post If true, call quadrant callback both pre and post. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). * \param [in] quadrant_fn This function controls the recursion, * which only continues deeper if this * callback returns true for a branch quadrant. @@ -333,11 +410,84 @@ typedef int (*p4est_search_partition_t) (p4est_t * p4est, * passed to the callback \b point_fn. * See \ref p4est_search_local for details. */ -void p4est_search_partition (p4est_t * p4est, int call_post, +void p4est_search_partition (p4est_t *p4est, int call_post, p4est_search_partition_t quadrant_fn, p4est_search_partition_t point_fn, - sc_array_t * points); + sc_array_t *points); + +/** Traverse some given global partition top-down. + * The partition can be that of any p4est, not necessarily known to the + * caller. This is not a collective function. It does not communicate. + * We proceed top-down through the partition, identically on all processors + * except for the results of two user-provided callbacks. The recursion will only + * go down branches that are split between multiple processors. The callback + * functions can be used to stop a branch recursion even for split branches. + * This function offers the option to search for arbitrary user-defined points + * analogously to \ref p4est_search_local. + * \note Traversing the whole given partition will be at least O(P), + * so sensible use of the callback function is advised to cut it short. + * \param [in] gfq Partition offsets to traverse. Length \a nmemb + 1. + * \param [in] gfp Partition position to traverse. Length \a nmemb + 1. + * \param [in] nmemb Number of processors encoded in \a gfq (plus one). + * \param [in] num_trees Tree number must match the contents of \a gfq. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). + * \param [in] user We pass a dummy p4est to the callbacks whose only + * valid element is its user_pointer set to \a user. + * \param [in] quadrant_fn This function controls the recursion, + * which only continues deeper if this + * callback returns true for a branch quadrant. + * It is allowed to set this to NULL. + * \param [in] point_fn This function decides per-point whether it is + * followed down the recursion. + * Must be non-NULL if \b points are not NULL. + * \param [in] points User-provided array of \b points that are + * passed to the callback \b point_fn. + * See \ref p4est_search_local for details. + */ +void p4est_search_partition_gfx + (const p4est_gloidx_t *gfq, const p4est_quadrant_t *gfp, + int nmemb, p4est_topidx_t num_trees, int call_post, void *user, + p4est_search_partition_t quadrant_fn, p4est_search_partition_t point_fn, + sc_array_t *points); + +/** Traverse some given global partition top-down. + * The partition can be that of any p4est, not necessarily known to the + * caller. This is not a collective function. It does not communicate. + * We proceed top-down through the partition, identically on all processors + * except for the results of two user-provided callbacks. The recursion will only + * go down branches that are split between multiple processors. The callback + * functions can be used to stop a branch recursion even for split branches. + * This function offers the option to search for arbitrary user-defined points + * analogously to \ref p4est_search_local. + * This function is similar to \ref p4est_search_partition_gfx, but does not + * require the \ref p4est_gloidx_t array gfq. If gfq is available, using + * \ref p4est_search_partition_gfx is recommended, because it is slightly faster. + * \note Traversing the whole given partition will be at least O(P), + * so sensible use of the callback function is advised to cut it short. + * \param [in] gfp Partition position to traverse. Length \a nmemb + 1. + * \param [in] nmemb Number of processors encoded in \a gfp (plus one). + * \param [in] num_trees Tree number must match the contents of \a gfp. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). + * \param [in] user We pass a dummy p4est to the callbacks whose only + * valid element is its user_pointer set to \a user. + * \param [in] quadrant_fn This function controls the recursion, + * which only continues deeper if this + * callback returns true for a branch quadrant. + * It is allowed to set this to NULL. + * \param [in] point_fn This function decides per-point whether it is + * followed down the recursion. + * Must be non-NULL if \b points are not NULL. + * \param [in] points User-provided array of \b points that are + * passed to the callback \b point_fn. + * See \ref p4est_search_local for details. + */ +void p4est_search_partition_gfp + (const p4est_quadrant_t *gfp, int nmemb, p4est_topidx_t num_trees, + int call_post, void *user, p4est_search_partition_t quadrant_fn, + p4est_search_partition_t point_fn, sc_array_t *points); /** Callback function for the top-down search through the whole forest. * \param [in] p4est The forest to search. @@ -433,7 +583,8 @@ typedef int (*p4est_search_all_t) (p4est_t * p4est, * will be faster since it employs specific local optimizations. * * \param [in] p4est The forest to be searched. - * \param [in] call_post If true, call quadrant callback both pre and post. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). * \param [in] quadrant_fn Executed once for each quadrant that is entered. * If the callback returns false, this quadrant and * its descendants are excluded from the search, and diff --git a/src/p4est_to_p8est.h b/src/p4est_to_p8est.h index 2855fc0fa..4f3ad0cc4 100644 --- a/src/p4est_to_p8est.h +++ b/src/p4est_to_p8est.h @@ -64,8 +64,28 @@ #define P4EST_QUADRANT_INIT P8EST_QUADRANT_INIT #define P4EST_LEAF_IS_FIRST_IN_TREE P8EST_LEAF_IS_FIRST_IN_TREE +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +#define P4EST_FILE_MAGIC_NUMBER P8EST_FILE_MAGIC_NUMBER +#define P4EST_FILE_METADATA_BYTES P8EST_FILE_METADATA_BYTES +#define P4EST_FILE_MAGIC_BYTES P8EST_FILE_MAGIC_BYTES +#define P4EST_FILE_VERSION_STR_BYTES P8EST_FILE_VERSION_STR_BYTES +#define P4EST_FILE_ARRAY_METADATA_BYTES P8EST_FILE_ARRAY_METADATA_BYTES +#define P4EST_FILE_ARRAY_METADATA_CHARS P8EST_FILE_ARRAY_METADATA_CHARS +#define P4EST_FILE_BYTE_DIV P8EST_FILE_BYTE_DIV +#define P4EST_FILE_MAX_NUM_PAD_BYTES P8EST_FILE_MAX_NUM_PAD_BYTES +#define P4EST_FILE_USER_STRING_BYTES P8EST_FILE_USER_STRING_BYTES +#define P4EST_FILE_FIELD_HEADER_BYTES P8EST_FILE_FIELD_HEADER_BYTES +#define P4EST_FILE_MAX_GLOBAL_QUAD P8EST_FILE_MAX_GLOBAL_QUAD +#define P4EST_FILE_MAX_BLOCK_SIZE P8EST_FILE_MAX_BLOCK_SIZE +#define P4EST_FILE_MAX_FIELD_ENTRY_SIZE P8EST_FILE_MAX_FIELD_ENTRY_SIZE + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + /* redefine enums */ +#define P4EST_CONNECT_SELF P8EST_CONNECT_SELF #define P4EST_CONNECT_FACE P8EST_CONNECT_FACE +#define P4EST_CONNECT_ALMOST P8EST_CONNECT_ALMOST #define P4EST_CONNECT_CORNER P8EST_CONNECT_CORNER #define P4EST_CONNECT_FULL P8EST_CONNECT_FULL #define P4EST_CONN_ENCODE_NONE P8EST_CONN_ENCODE_NONE @@ -78,6 +98,32 @@ #define P4EST_WRAP_REFINE P8EST_WRAP_REFINE #define P4EST_WRAP_COARSEN P8EST_WRAP_COARSEN +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +#define P4EST_FILE_ERR_SUCCESS P8EST_FILE_ERR_SUCCESS +#define P4EST_FILE_ERR_FILE P8EST_FILE_ERR_FILE +#define P4EST_FILE_ERR_NOT_SAME P8EST_FILE_ERR_NOT_SAME +#define P4EST_FILE_ERR_AMODE P8EST_FILE_ERR_AMODE +#define P4EST_FILE_ERR_NO_SUCH_FILE P8EST_FILE_ERR_NO_SUCH_FILE +#define P4EST_FILE_ERR_FILE_EXIST P8EST_FILE_ERR_FILE_EXIST +#define P4EST_FILE_ERR_BAD_FILE P8EST_FILE_ERR_BAD_FILE +#define P4EST_FILE_ERR_ACCESS P8EST_FILE_ERR_ACCESS +#define P4EST_FILE_ERR_NO_SPACE P8EST_FILE_ERR_NO_SPACE +#define P4EST_FILE_ERR_QUOTA P8EST_FILE_ERR_QUOTA +#define P4EST_FILE_ERR_READ_ONLY P8EST_FILE_ERR_READ_ONLY +#define P4EST_FILE_ERR_IN_USE P8EST_FILE_ERR_IN_USE +#define P4EST_FILE_ERR_IO P8EST_FILE_ERR_IO +#define P4EST_FILE_ERR_FORMAT P8EST_FILE_ERR_FORMAT +#define P4EST_FILE_ERR_SECTION_TYPE P8EST_FILE_ERR_SECTION_TYPE +#define P4EST_FILE_ERR_CONN P8EST_FILE_ERR_CONN +#define P4EST_FILE_ERR_P4EST P8EST_FILE_ERR_P8EST +#define P4EST_FILE_ERR_IN_DATA P8EST_FILE_ERR_IN_DATA +#define P4EST_FILE_ERR_COUNT P8EST_FILE_ERR_COUNT +#define P4EST_FILE_ERR_UNKNOWN P8EST_FILE_ERR_UNKNOWN +#define P4EST_FILE_ERR_LASTCODE P8EST_FILE_ERR_LASTCODE + +#endif + /* redefine types */ #ifdef P4EST_BACKWARD_DEALII #define p4est_balance_type_t p8est_balance_type_t @@ -87,7 +133,9 @@ #define p4est_connectivity_t p8est_connectivity_t #define p4est_corner_transform_t p8est_corner_transform_t #define p4est_corner_info_t p8est_corner_info_t +#define p4est_neighbor_transform_t p8est_neighbor_transform_t #define p4est_geometry_t p8est_geometry_t +#define p4est_geometry_destroy_t p8est_geometry_destroy_t #define p4est_t p8est_t #define p4est_tree_t p8est_tree_t #define p4est_quadrant_t p8est_quadrant_t @@ -114,8 +162,10 @@ #define p4est_iter_corner_t p8est_iter_corner_t #define p4est_iter_corner_side_t p8est_iter_corner_side_t #define p4est_iter_corner_info_t p8est_iter_corner_info_t +#define p4est_mesh_params_t p8est_mesh_params_t #define p4est_search_query_t p8est_search_query_t #define p4est_search_local_t p8est_search_local_t +#define p4est_search_reorder_t p8est_search_reorder_t #define p4est_search_partition_t p8est_search_partition_t #define p4est_search_all_t p8est_search_all_t #define p4est_build p8est_build @@ -127,7 +177,10 @@ #define p4est_wrap_t p8est_wrap_t #define p4est_wrap_leaf_t p8est_wrap_leaf_t #define p4est_wrap_flags_t p8est_wrap_flags_t +#define p4est_wrap_params_t p8est_wrap_params_t #define p4est_vtk_context_t p8est_vtk_context_t +#define p4est_file_context_t p8est_file_context_t +#define p4est_file_section_metadata_t p8est_file_section_metadata_t /* redefine external variables */ #define p4est_face_corners p8est_face_corners @@ -166,6 +219,12 @@ #define p4est_expand_face_transform p8est_expand_face_transform #define p4est_find_face_transform p8est_find_face_transform #define p4est_find_corner_transform p8est_find_corner_transform +#define p4est_neighbor_transform_coordinates \ + p8est_neighbor_transform_coordinates +#define p4est_neighbor_transform_coordinates_reverse \ + p8est_neighbor_transform_coordinates_reverse +#define p4est_connectivity_get_neighbor_transforms \ + p8est_connectivity_get_neighbor_transforms #define p4est_corner_array_index p8est_corner_array_index #define p4est_connectivity_reorder p8est_connectivity_reorder #define p4est_connectivity_reorder_newid \ @@ -196,6 +255,7 @@ #define p4est_connect_type_string p8est_connect_type_string #define p4est_tree_array_index p8est_tree_array_index #define p4est_quadrant_array_index p8est_quadrant_array_index +#define p4est_quadrant_array_push_copy p8est_quadrant_array_push_copy #define p4est_quadrant_array_push p8est_quadrant_array_push #define p4est_quadrant_mempool_alloc p8est_quadrant_mempool_alloc #define p4est_quadrant_list_pop p8est_quadrant_list_pop @@ -226,6 +286,8 @@ #define p4est_quadrant_set_morton_ext128 p8est_quadrant_set_morton_ext128 #define p4est_new_ext p8est_new_ext #define p4est_mesh_new_ext p8est_mesh_new_ext +#define p4est_mesh_new_params p8est_mesh_new_params +#define p4est_mesh_params_init p8est_mesh_params_init #define p4est_copy_ext p8est_copy_ext #define p4est_refine_ext p8est_refine_ext #define p4est_coarsen_ext p8est_coarsen_ext @@ -237,6 +299,13 @@ #define p4est_load_ext p8est_load_ext #define p4est_source_ext p8est_source_ext +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +#define p4est_file_open_read_ext p8est_file_open_read_ext +#define p4est_file_read_field_ext p8est_file_read_field_ext + +#endif + /* functions in p4est_iterate */ #define p4est_iterate p8est_iterate #define p4est_iterate_ext p8est_iterate_ext @@ -255,6 +324,7 @@ #define p4est_quadrant_overlaps p8est_quadrant_overlaps #define p4est_quadrant_is_equal_piggy p8est_quadrant_is_equal_piggy #define p4est_quadrant_compare p8est_quadrant_compare +#define p4est_coordinates_compare p8est_coordinates_compare #define p4est_quadrant_disjoint p8est_quadrant_disjoint #define p4est_quadrant_compare_piggy p8est_quadrant_compare_piggy #define p4est_quadrant_compare_local_num p8est_quadrant_compare_local_num @@ -268,11 +338,13 @@ #define p4est_quadrant_contains_node p8est_quadrant_contains_node #define p4est_quadrant_ancestor_id p8est_quadrant_ancestor_id #define p4est_quadrant_child_id p8est_quadrant_child_id +#define p4est_coordinates_is_inside_root p8est_coordinates_is_inside_root #define p4est_quadrant_is_inside_root p8est_quadrant_is_inside_root #define p4est_quadrant_is_inside_3x3 p8est_quadrant_is_inside_3x3 #define p4est_quadrant_is_outside_face p8est_quadrant_is_outside_face #define p4est_quadrant_is_outside_corner p8est_quadrant_is_outside_corner #define p4est_quadrant_is_node p8est_quadrant_is_node +#define p4est_coordinates_is_valid p8est_coordinates_is_valid #define p4est_quadrant_is_valid p8est_quadrant_is_valid #define p4est_quadrant_is_extended p8est_quadrant_is_extended #define p4est_quadrant_is_sibling p8est_quadrant_is_sibling @@ -313,6 +385,8 @@ #define p4est_quadrant_corner_descendant p8est_quadrant_corner_descendant #define p4est_nearest_common_ancestor p8est_nearest_common_ancestor #define p4est_nearest_common_ancestor_D p8est_nearest_common_ancestor_D +#define p4est_coordinates_transform_face \ + p8est_coordinates_transform_face #define p4est_quadrant_transform_face p8est_quadrant_transform_face #define p4est_quadrant_touches_corner p8est_quadrant_touches_corner #define p4est_quadrant_transform_corner p8est_quadrant_transform_corner @@ -322,6 +396,14 @@ #define p4est_quadrant_successor p8est_quadrant_successor #define p4est_quadrant_predecessor p8est_quadrant_predecessor #define p4est_quadrant_srand p8est_quadrant_srand +#define p4est_neighbor_transform_quadrant \ + p8est_neighbor_transform_quadrant +#define p4est_neighbor_transform_quadrant_reverse \ + p8est_neighbor_transform_quadrant_reverse +#define p4est_quadrant_is_ancestor_face \ + p8est_quadrant_is_ancestor_face +#define p4est_quadrant_is_ancestor_corner \ + p8est_quadrant_is_ancestor_corner /* functions in p4est_search */ #define p4est_find_partition p8est_find_partition @@ -332,7 +414,10 @@ #define p4est_find_range_boundaries p8est_find_range_boundaries #define p4est_search p8est_search #define p4est_search_local p8est_search_local +#define p4est_search_reorder p8est_search_reorder #define p4est_search_partition p8est_search_partition +#define p4est_search_partition_gfx p8est_search_partition_gfx +#define p4est_search_partition_gfp p8est_search_partition_gfp #define p4est_search_all p8est_search_all #define p4est_build_new p8est_build_new #define p4est_build_init_add p8est_build_init_add @@ -340,6 +425,7 @@ #define p4est_build_complete p8est_build_complete /* functions in p4est_algorithms */ +#define p4est_quadrant_mempool_new p8est_quadrant_mempool_new #define p4est_quadrant_init_data p8est_quadrant_init_data #define p4est_quadrant_free_data p8est_quadrant_free_data #define p4est_quadrant_checksum p8est_quadrant_checksum @@ -364,6 +450,7 @@ #define p4est_partition_correction p8est_partition_correction #define p4est_partition_for_coarsening p8est_partition_for_coarsening #define p4est_partition_given p8est_partition_given +#define p4est_quadrant_on_face_boundary p8est_quadrant_on_face_boundary /* functions in p4est_communication */ #define p4est_comm_parallel_env_assign p8est_comm_parallel_env_assign @@ -376,16 +463,19 @@ #define p4est_comm_parallel_env_reduce_ext p8est_comm_parallel_env_reduce_ext #define p4est_comm_count_quadrants p8est_comm_count_quadrants #define p4est_comm_global_partition p8est_comm_global_partition +#define p4est_comm_global_first_quadrant p8est_comm_global_first_quadrant #define p4est_comm_count_pertree p8est_comm_count_pertree #define p4est_comm_is_empty p8est_comm_is_empty +#define p4est_comm_is_empty_gfq p8est_comm_is_empty_gfq +#define p4est_comm_is_empty_gfp p8est_comm_is_empty_gfp #define p4est_comm_is_contained p8est_comm_is_contained #define p4est_comm_is_owner p8est_comm_is_owner +#define p4est_comm_is_owner_gfp p8est_comm_is_owner_gfp #define p4est_comm_find_owner p8est_comm_find_owner #define p4est_comm_tree_info p8est_comm_tree_info #define p4est_comm_neighborhood_owned p8est_comm_neighborhood_owned #define p4est_comm_sync_flag p8est_comm_sync_flag #define p4est_comm_checksum p8est_comm_checksum -#define p4est_comm_checksum_partition p8est_comm_checksum_partition #define p4est_transfer_fixed p8est_transfer_fixed #define p4est_bsearch_partition p8est_bsearch_partition #define p4est_transfer_fixed_begin p8est_transfer_fixed_begin @@ -401,10 +491,31 @@ /* functions in p4est_io */ #define p4est_deflate_quadrants p8est_deflate_quadrants #define p4est_inflate p8est_inflate +#define p4est_inflate_null p8est_inflate_null + +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +#define p4est_file_open_create p8est_file_open_create +#define p4est_file_open_append p8est_file_open_append +#define p4est_file_open_read p8est_file_open_read +#define p4est_file_write_block p8est_file_write_block +#define p4est_file_read_block p8est_file_read_block +#define p4est_file_write_field p8est_file_write_field +#define p4est_file_read_field p8est_file_read_field +#define p4est_file_info p8est_file_info +#define p4est_file_error_string p8est_file_error_string +#define p4est_file_write_p4est p8est_file_write_p8est +#define p4est_file_read_p4est p8est_file_read_p8est +#define p4est_file_write_connectivity p8est_file_write_connectivity +#define p4est_file_read_connectivity p8est_file_read_connectivity +#define p4est_file_close p8est_file_close + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ /* functions in p4est_geometry */ #define p4est_geometry_destroy p8est_geometry_destroy #define p4est_geometry_new_connectivity p8est_geometry_new_connectivity +#define p4est_geometry_connectivity_X p8est_geometry_connectivity_X /* functions in p4est_vtk */ #define p4est_vtk_context_new p8est_vtk_context_new @@ -414,6 +525,7 @@ #define p4est_vtk_context_set_continuous p8est_vtk_context_set_continuous #define p4est_vtk_write_file p8est_vtk_write_file #define p4est_vtk_write_header p8est_vtk_write_header +#define p4est_vtk_write_header_ho p8est_vtk_write_header_ho #define p4est_vtk_write_cell_dataf p8est_vtk_write_cell_dataf #define p4est_vtk_write_cell_datav p8est_vtk_write_cell_datav #define p4est_vtk_write_cell_data p8est_vtk_write_cell_data @@ -425,6 +537,7 @@ #define p4est_quadrant_find_owner p8est_quadrant_find_owner #define p4est_ghost_memory_used p8est_ghost_memory_used #define p4est_ghost_new p8est_ghost_new +#define p4est_ghost_new_local p8est_ghost_new_local #define p4est_ghost_destroy p8est_ghost_destroy #define p4est_ghost_exchange_data p8est_ghost_exchange_data #define p4est_ghost_exchange_data_begin p8est_ghost_exchange_data_begin @@ -467,7 +580,8 @@ #define p4est_lnodes_share_all p8est_lnodes_share_all #define p4est_lnodes_buffer_destroy p8est_lnodes_buffer_destroy #define p4est_lnodes_rank_array_index p8est_lnodes_rank_array_index -#define p4est_lnodes_rank_array_index_int p8est_lnodes_rank_array_index_int +#define p4est_lnodes_rank_array_index_int \ + p8est_lnodes_rank_array_index_int #define p4est_lnodes_global_index p8est_lnodes_global_index /* functions in p4est_mesh */ @@ -488,15 +602,19 @@ #define p4est_balance_seeds p8est_balance_seeds /* functions in p4est_wrap */ +#define p4est_wrap_params_init p8est_wrap_params_init #define p4est_wrap_new_conn p8est_wrap_new_conn #define p4est_wrap_new_p4est p8est_wrap_new_p8est +#define p4est_wrap_new_p4est_params p8est_wrap_new_p8est_params #define p4est_wrap_new_brick p8est_wrap_new_brick #define p4est_wrap_new_world p8est_wrap_new_world #define p4est_wrap_new_ext p8est_wrap_new_ext +#define p4est_wrap_new_params p8est_wrap_new_params #define p4est_wrap_new_copy p8est_wrap_new_copy #define p4est_wrap_destroy p8est_wrap_destroy #define p4est_wrap_set_hollow p8est_wrap_set_hollow #define p4est_wrap_set_coarsen_delay p8est_wrap_set_coarsen_delay +#define p4est_wrap_set_partitioning p8est_wrap_set_partitioning #define p4est_wrap_get_ghost p8est_wrap_get_ghost #define p4est_wrap_get_mesh p8est_wrap_get_mesh #define p4est_wrap_mark_refine p8est_wrap_mark_refine diff --git a/src/p4est_vtk.c b/src/p4est_vtk.c index 29dc12596..03805fa19 100644 --- a/src/p4est_vtk.c +++ b/src/p4est_vtk.c @@ -22,14 +22,33 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* + * There are several configure options that affect the VTK output. + * --enable-vtk-binary (default) uses binary/base64 encoding. + * --enable-vtk-compression (default) uses zlib compression. + * --enable-vtk-doubles (default) uses 64-bit binary reals. + * These may be disabled using the --disable-vtk-* forms. + * + * These options translate into the preprocessor #defines + * P4EST_ENABLE_VTK_BINARY, + * P4EST_ENABLE_VTK_COMPRESSION, + * P4EST_ENABLE_VTK_DOUBLES. + * The older version of the macros without _ENABLE_ is obsolete. + * + * We define further macros of the form P4EST_VTK_ in this file. + * These are local to the code in this file and not exported. + */ + #ifdef P4_TO_P8 #include #include #define P4EST_VTK_CELL_TYPE 11 /* VTK_VOXEL */ +#define P4EST_VTK_CELL_TYPE_HO 72 /* VTK_LAGRANGE_HEXAHEDRON */ #else #include #include #define P4EST_VTK_CELL_TYPE 8 /* VTK_PIXEL */ +#define P4EST_VTK_CELL_TYPE_HO 70 /* VTK_LAGRANGE_QUADRILATERAL */ #endif /* !P4_TO_P8 */ /* default parameters for the vtk context */ @@ -122,7 +141,7 @@ static #define p4est_vtk_context p8est_vtk_context #endif -#ifndef P4EST_VTK_DOUBLES +#ifndef P4EST_ENABLE_VTK_DOUBLES #define P4EST_VTK_FLOAT_NAME "Float32" #define P4EST_VTK_FLOAT_TYPE float #else @@ -130,7 +149,7 @@ static #define P4EST_VTK_FLOAT_TYPE double #endif -#ifndef P4EST_VTK_BINARY +#ifndef P4EST_ENABLE_VTK_BINARY #define P4EST_VTK_ASCII 1 #define P4EST_VTK_FORMAT_STRING "ascii" #else @@ -140,14 +159,14 @@ static int p4est_vtk_write_binary (FILE * vtkfile, char *numeric_data, size_t byte_length) { -#ifndef P4EST_VTK_COMPRESSION +#ifndef P4EST_ENABLE_VTK_COMPRESSION return sc_vtk_write_binary (vtkfile, numeric_data, byte_length); #else return sc_vtk_write_compressed (vtkfile, numeric_data, byte_length); -#endif /* P4EST_VTK_COMPRESSION */ +#endif /* P4EST_ENABLE_VTK_COMPRESSION */ } -#endif /* P4EST_VTK_BINARY */ +#endif /* P4EST_ENABLE_VTK_BINARY */ /** Opaque context type for writing VTK output with multiple function calls. * @@ -460,7 +479,7 @@ p4est_vtk_write_header (p4est_vtk_context_t * cont) fprintf (cont->vtufile, "\n"); fprintf (cont->vtufile, "vtufile, " compressor=\"vtkZLibDataCompressor\""); #endif #ifdef SC_IS_BIGENDIAN @@ -617,7 +636,7 @@ p4est_vtk_write_header (p4est_vtk_context_t * cont) wz = float_data[3 * il + 2]; fprintf (cont->vtufile, -#ifdef P4EST_VTK_DOUBLES +#ifdef P4EST_ENABLE_VTK_DOUBLES " %24.16e %24.16e %24.16e\n", #else " %16.8e %16.8e %16.8e\n", @@ -768,7 +787,7 @@ p4est_vtk_write_header (p4est_vtk_context_t * cont) fprintf (cont->pvtufile, "\n"); fprintf (cont->pvtufile, "pvtufile, " compressor=\"vtkZLibDataCompressor\""); #endif #ifdef SC_IS_BIGENDIAN @@ -811,6 +830,405 @@ p4est_vtk_write_header (p4est_vtk_context_t * cont) return cont; } +#ifdef P4_TO_P8 +/* Based on + * https://github.com/Kitware/VTK/blob/99770c75c2df471c456323d66a4a0bd154cf3a82 + * /Common/DataModel/vtkHigherOrderHexahedron.cxx#L611 + */ +static int +point_index_from_ijk (int i, int j, int k, const int *order) +{ + int ibdy = (i == 0 || i == order[0]); + int jbdy = (j == 0 || j == order[1]); + int kbdy = (k == 0 || k == order[2]); + /* How many boundaries do we lie on at once? */ + int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0) + (kbdy ? 1 : 0); + + if (nbdy == 3) { /* Vertex DOF */ + /* ijk is a corner node. Return the proper index (somewhere in [0,7]): */ + return (i ? (j ? 2 : 1) : (j ? 3 : 0)) + (k ? 4 : 0); + } + + int offset = 8; + if (nbdy == 2) { /* Edge DOF */ + if (!ibdy) { /* On i axis */ + return (i - 1) + (j ? order[0] + order[1] - 2 : 0) + + (k ? 2 * (order[0] + order[1] - 2) : 0) + offset; + } + if (!jbdy) { /* On j axis */ + return (j - 1) + (i ? order[0] - 1 : 2 * (order[0] - 1) + order[1] - + 1) + (k ? 2 * (order[0] + order[1] - 2) : 0) + offset; + } + /* !kbdy, On k axis */ + offset += 4 * (order[0] - 1) + 4 * (order[1] - 1); + return (k - 1) + (order[2] - 1) * (i ? (j ? 3 : 1) : (j ? 2 : 0)) + + offset; + } + + offset += 4 * (order[0] + order[1] + order[2] - 3); + if (nbdy == 1) { /* Face DOF */ + if (ibdy) { /* On i-normal face */ + return (j - 1) + ((order[1] - 1) * (k - 1)) + + (i ? (order[1] - 1) * (order[2] - 1) : 0) + offset; + } + offset += 2 * (order[1] - 1) * (order[2] - 1); + if (jbdy) { /* On j-normal face */ + return (i - 1) + ((order[0] - 1) * (k - 1)) + + (j ? (order[2] - 1) * (order[0] - 1) : 0) + offset; + } + offset += 2 * (order[2] - 1) * (order[0] - 1); + /* kbdy, On k-normal face */ + return (i - 1) + ((order[0] - 1) * (j - 1)) + + (k ? (order[0] - 1) * (order[1] - 1) : 0) + offset; + } + + /* nbdy == 0: Body DOF */ + offset += 2 * + ((order[1] - 1) * (order[2] - 1) + (order[2] - 1) * (order[0] - 1) + + (order[0] - 1) * (order[1] - 1)); + return offset + (i - 1) + (order[0] - 1) * + ((j - 1) + (order[1] - 1) * ((k - 1))); +} +#else +/* Based on + * https://github.com/Kitware/VTK/blob/99770c75c2df471c456323d66a4a0bd154cf3a82 + * /Common/DataModel/vtkHigherOrderQuadrilateral.cxx#L446 + */ +static int +point_index_from_ijk (int i, int j, const int *order) +{ + int ibdy = (i == 0 || i == order[0]); + int jbdy = (j == 0 || j == order[1]); + /* How many boundaries do we lie on at once? */ + int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0); + + if (nbdy == 2) { /* Vertex DOF */ + /* ijk is a corner node. Return the proper index (somewhere in [0,7]): */ + return (i ? (j ? 2 : 1) : (j ? 3 : 0)); + } + + int offset = 4; + if (nbdy == 1) { /* Edge DOF */ + if (!ibdy) { /* On i axis */ + return (i - 1) + (j ? order[0] - 1 + order[1] - 1 : 0) + offset; + } + if (!jbdy) { /* On j axis */ + return (j - 1) + (i ? order[0] - 1 : 2 * (order[0] - 1) + order[1] - + 1) + offset; + } + } + + offset += 2 * (order[0] - 1 + order[1] - 1); + /* nbdy == 0: Face DOF */ + return offset + (i - 1) + (order[0] - 1) * ((j - 1)); +} +#endif + +p4est_vtk_context_t * +p4est_vtk_write_header_ho (p4est_vtk_context_t * cont, sc_array_t * positions, + int Nnodes1D) +{ + /* positions in order of: x,y,z,x,y,z */ + int mpirank; + const char *filename; + p4est_locidx_t Ncells; + p4est_t *p4est; + p4est_locidx_t *locidx_data; +#ifdef P4EST_VTK_ASCII + double wx, wy, wz = 0.; +#else + P4EST_VTK_FLOAT_TYPE *float_data; + int retval; + uint8_t *uint8_data; +#endif + int i, j; +#if defined P4_TO_P8 || defined P4EST_VTK_ASCII + int k; +#endif + int order[P4EST_DIM]; + p4est_locidx_t Npoints, Npointscell; + p4est_locidx_t sk, il; + + /* check a whole bunch of assertions, here and below */ + P4EST_ASSERT (cont != NULL); + P4EST_ASSERT (!cont->writing); + + /* from now on this context is officially in use for writing */ + cont->writing = 1; + + /* grab context variables */ + p4est = cont->p4est; + filename = cont->filename; + P4EST_ASSERT (filename != NULL); + + /* grab details from the forest */ + P4EST_ASSERT (p4est != NULL); + mpirank = p4est->mpirank; + Ncells = p4est->local_num_quadrants; + + cont->num_corners = P4EST_CHILDREN * Ncells; + cont->nodes = NULL; +#ifdef P4_TO_P8 + Npointscell = Nnodes1D * Nnodes1D * Nnodes1D; +#else + Npointscell = Nnodes1D * Nnodes1D; +#endif + cont->num_points = Npoints = Npointscell * Ncells; + cont->node_to_corner = NULL; + + /* Have each proc write to its own file */ + snprintf (cont->vtufilename, BUFSIZ, "%s_%04d.vtu", filename, mpirank); + /* Use "w" for writing the initial part of the file. + * For further parts, use "r+" and fseek so write_compressed succeeds. + */ + cont->vtufile = fopen (cont->vtufilename, "wb"); + if (cont->vtufile == NULL) { + P4EST_LERRORF ("Could not open %s for output\n", cont->vtufilename); + p4est_vtk_context_destroy (cont); + return NULL; + } + + fprintf (cont->vtufile, "\n"); + fprintf (cont->vtufile, + "vtufile, " compressor=\"vtkZLibDataCompressor\""); +#endif +#ifdef SC_IS_BIGENDIAN + fprintf (cont->vtufile, " byte_order=\"BigEndian\">\n"); +#else + fprintf (cont->vtufile, " byte_order=\"LittleEndian\">\n"); +#endif + fprintf (cont->vtufile, " \n"); + fprintf (cont->vtufile, + " \n", + (long long) Npoints, (long long) Ncells); + fprintf (cont->vtufile, " \n"); + + /* write point position data */ + fprintf (cont->vtufile, " \n", + P4EST_VTK_FLOAT_NAME, P4EST_VTK_FORMAT_STRING); + +#ifdef P4EST_VTK_ASCII + for (il = 0; il < Npoints; ++il) { + wx = *(double *) sc_array_index (positions, (il * P4EST_DIM)); + wy = *(double *) sc_array_index (positions, (il * P4EST_DIM) + 1); +#ifdef P4_TO_P8 + wz = *(double *) sc_array_index (positions, (il * P4EST_DIM) + 2); +#endif + + fprintf (cont->vtufile, +#ifdef P4EST_ENABLE_VTK_DOUBLES + " %24.16e %24.16e %24.16e\n", +#else + " %16.8e %16.8e %16.8e\n", +#endif + wx, wy, wz); + } +#else + float_data = P4EST_ALLOC (P4EST_VTK_FLOAT_TYPE, 3 * Npoints); + fprintf (cont->vtufile, " "); + /* TODO: Don't allocate the full size of the array, only allocate + * the chunk that will be passed to zlib and do this a chunk + * at a time. + */ + for (il = 0; il < Npoints; ++il) { + for (j = 0; j < P4EST_DIM; ++j) { + float_data[il * 3 + j] = (P4EST_VTK_FLOAT_TYPE) * + ((double *) sc_array_index (positions, (il * P4EST_DIM) + j)); + } +#ifndef P4_TO_P8 + float_data[il * 3 + 2] = 0.; +#endif + } + retval = p4est_vtk_write_binary (cont->vtufile, (char *) float_data, + sizeof (*float_data) * 3 * Npoints); + fprintf (cont->vtufile, "\n"); + if (retval) { + P4EST_LERROR (P4EST_STRING "_vtk: Error encoding points\n"); + p4est_vtk_context_destroy (cont); + P4EST_FREE (float_data); + return NULL; + } + P4EST_FREE (float_data); +#endif + + fprintf (cont->vtufile, " \n"); + fprintf (cont->vtufile, " \n"); + fprintf (cont->vtufile, " \n"); + + /* write connectivity data */ + fprintf (cont->vtufile, + " \n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING); + locidx_data = P4EST_ALLOC (p4est_locidx_t, Npoints); + order[0] = order[1] = Nnodes1D - 1; +#ifdef P4_TO_P8 + order[2] = order[0]; +#endif + for (sk = 0, il = 0; il < Ncells; ++il) { +#ifdef P4_TO_P8 + for (k = 0; k < Nnodes1D; ++k) { +#endif + for (j = 0; j < Nnodes1D; ++j) { + for (i = 0; i < Nnodes1D; ++sk, ++i) { + locidx_data[il * Npointscell + +#ifdef P4_TO_P8 + point_index_from_ijk (i, j, k, order) +#else + point_index_from_ijk (i, j, order) +#endif + ] = sk; + } + } +#ifdef P4_TO_P8 + } +#endif + } +#ifdef P4EST_VTK_ASCII + for (sk = 0, il = 0; il < Ncells; ++il) { + fprintf (cont->vtufile, " "); + for (k = 0; k < Npointscell; ++sk, ++k) { + fprintf (cont->vtufile, " %lld", (long long) locidx_data[sk]); + } + fprintf (cont->vtufile, "\n"); + } +#else + fprintf (cont->vtufile, " "); + retval = + p4est_vtk_write_binary (cont->vtufile, (char *) locidx_data, + sizeof (p4est_locidx_t) * Npoints); + fprintf (cont->vtufile, "\n"); + if (retval) { + P4EST_LERROR (P4EST_STRING "_vtk: Error encoding connectivity\n"); + p4est_vtk_context_destroy (cont); + return NULL; + } +#endif + P4EST_FREE (locidx_data); + fprintf (cont->vtufile, " \n"); + + /* write offset data */ + fprintf (cont->vtufile, " \n", P4EST_VTK_LOCIDX, P4EST_VTK_FORMAT_STRING); +#ifdef P4EST_VTK_ASCII + fprintf (cont->vtufile, " "); + for (il = 1, sk = 1; il <= Ncells; ++il, ++sk) { + fprintf (cont->vtufile, " %lld", (long long) (Npointscell * il)); + if (!(sk % 8) && il != Ncells) + fprintf (cont->vtufile, "\n "); + } + fprintf (cont->vtufile, "\n"); +#else + locidx_data = P4EST_ALLOC (p4est_locidx_t, Ncells); + for (il = 1; il <= Ncells; ++il) + locidx_data[il - 1] = Npointscell * il; /* same type */ + + fprintf (cont->vtufile, " "); + retval = p4est_vtk_write_binary (cont->vtufile, (char *) locidx_data, + sizeof (p4est_locidx_t) * Ncells); + fprintf (cont->vtufile, "\n"); + + P4EST_FREE (locidx_data); + + if (retval) { + P4EST_LERROR (P4EST_STRING "_vtk: Error encoding offsets\n"); + p4est_vtk_context_destroy (cont); + return NULL; + } +#endif + fprintf (cont->vtufile, " \n"); + + /* write type data */ + fprintf (cont->vtufile, " \n", P4EST_VTK_FORMAT_STRING); +#ifdef P4EST_VTK_ASCII + fprintf (cont->vtufile, " "); + for (il = 0, sk = 1; il < Ncells; ++il, ++sk) { + fprintf (cont->vtufile, " %d", P4EST_VTK_CELL_TYPE_HO); + if (!(sk % 20) && il != (Ncells - 1)) + fprintf (cont->vtufile, "\n "); + } + fprintf (cont->vtufile, "\n"); +#else + uint8_data = P4EST_ALLOC (uint8_t, Ncells); + for (il = 0; il < Ncells; ++il) + uint8_data[il] = P4EST_VTK_CELL_TYPE_HO; + + fprintf (cont->vtufile, " "); + retval = p4est_vtk_write_binary (cont->vtufile, (char *) uint8_data, + sizeof (*uint8_data) * Ncells); + fprintf (cont->vtufile, "\n"); + + P4EST_FREE (uint8_data); + + if (retval) { + P4EST_LERROR (P4EST_STRING "_vtk: Error encoding types\n"); + p4est_vtk_context_destroy (cont); + return NULL; + } +#endif + fprintf (cont->vtufile, " \n"); + fprintf (cont->vtufile, " \n"); + + if (ferror (cont->vtufile)) { + P4EST_LERROR (P4EST_STRING "_vtk: Error writing header\n"); + p4est_vtk_context_destroy (cont); + return NULL; + } + + /* Only have the root write to the parallel vtk file */ + if (mpirank == 0) { + snprintf (cont->pvtufilename, BUFSIZ, "%s.pvtu", filename); + + cont->pvtufile = fopen (cont->pvtufilename, "wb"); + if (!cont->pvtufile) { + P4EST_LERRORF ("Could not open %s for output\n", cont->pvtufilename); + p4est_vtk_context_destroy (cont); + return NULL; + } + + fprintf (cont->pvtufile, "\n"); + fprintf (cont->pvtufile, + "pvtufile, " compressor=\"vtkZLibDataCompressor\""); +#endif +#ifdef SC_IS_BIGENDIAN + fprintf (cont->pvtufile, " byte_order=\"BigEndian\">\n"); +#else + fprintf (cont->pvtufile, " byte_order=\"LittleEndian\">\n"); +#endif + + fprintf (cont->pvtufile, " \n"); + fprintf (cont->pvtufile, " \n"); + fprintf (cont->pvtufile, " \n", + P4EST_VTK_FLOAT_NAME, P4EST_VTK_FORMAT_STRING); + fprintf (cont->pvtufile, " \n"); + + if (ferror (cont->pvtufile)) { + P4EST_LERROR (P4EST_STRING "_vtk: Error writing parallel header\n"); + p4est_vtk_context_destroy (cont); + return NULL; + } + + /* Create a master file for visualization in Visit; this will be used + * only in p4est_vtk_write_footer(). + */ + snprintf (cont->visitfilename, BUFSIZ, "%s.visit", filename); + cont->visitfile = fopen (cont->visitfilename, "wb"); + if (!cont->visitfile) { + P4EST_LERRORF ("Could not open %s for output\n", cont->visitfilename); + p4est_vtk_context_destroy (cont); + return NULL; + } + } + + return cont; +} + /** Write VTK point data. * * This function exports custom point data to the vtk file; it is functionally @@ -844,6 +1262,7 @@ p4est_vtk_write_point_datav (p4est_vtk_context_t * cont, const char *name, **names; p4est_vtk_context_t *list_end; sc_array_t **values; + p4est_locidx_t ecount; P4EST_ASSERT (cont != NULL && cont->writing); P4EST_ASSERT (cont->p4est != NULL); @@ -858,6 +1277,9 @@ p4est_vtk_write_point_datav (p4est_vtk_context_t * cont, values = P4EST_ALLOC (sc_array_t *, num_point_all); names = P4EST_ALLOC (const char *, num_point_all); + ecount = + cont->node_to_corner == NULL ? cont->num_points : cont->num_corners; + /* Gather point data. */ all = 0; scalar_strlen = 0; @@ -876,7 +1298,7 @@ p4est_vtk_write_point_datav (p4est_vtk_context_t * cont, P4EST_STRING "_vtk: Error: incorrect point scalar data type;" " scalar data must contain doubles."); - SC_CHECK_ABORT (values[all]->elem_count == (size_t) cont->num_corners, + SC_CHECK_ABORT (values[all]->elem_count == (size_t) ecount, P4EST_STRING "_vtk: Error: incorrect point scalar data count; see " P4EST_STRING "_vtk.h for more details."); @@ -899,7 +1321,7 @@ p4est_vtk_write_point_datav (p4est_vtk_context_t * cont, P4EST_STRING "_vtk: Error: incorrect point vector data type;" " vector data must contain doubles."); - SC_CHECK_ABORT (values[all]->elem_count == 3 * (size_t) cont->num_corners, + SC_CHECK_ABORT (values[all]->elem_count == 3 * (size_t) ecount, P4EST_STRING "_vtk: Error: incorrect point vector data count; see " P4EST_STRING "_vtk.h for more details."); @@ -1430,7 +1852,7 @@ p4est_vtk_write_point (p4est_vtk_context_t * cont, p4est_locidx_t il, ddl; int use_nodes; #ifdef P4EST_ENABLE_DEBUG - int Ncorners; + int ecount; int Nentries; #endif int Npoints; @@ -1441,19 +1863,18 @@ p4est_vtk_write_point (p4est_vtk_context_t * cont, p4est_locidx_t *ntc; P4EST_ASSERT (cont != NULL && cont->writing); -#ifdef P4EST_ENABLE_DEBUG - Ncorners = cont->num_corners; - Nentries = Ncorners * (is_vector ? 3 : 1); -#endif Npoints = cont->num_points; ntc = cont->node_to_corner; +#ifdef P4EST_ENABLE_DEBUG + ecount = ntc == NULL ? Npoints : cont->num_corners; + Nentries = ecount * (is_vector ? 3 : 1); +#endif P4EST_ASSERT (values != NULL && values->elem_count == (size_t) Nentries); if (ntc == NULL) { - /* we are writing a discontinuous field, possibly due to vertex scaling */ - P4EST_ASSERT (cont->num_corners == cont->num_points); - P4EST_ASSERT (cont->scale < 1. || !cont->continuous); + /* may be writing a discontinuous field, possibly due to vertex scaling */ + /* or we are writing higher order cells, relying on user coordinates */ use_nodes = 0; } else { @@ -1473,10 +1894,10 @@ p4est_vtk_write_point (p4est_vtk_context_t * cont, if (!is_vector) { for (il = 0; il < Npoints; ++il) { ddl = use_nodes ? ntc[il] : il; - P4EST_ASSERT (0 <= ddl && ddl < Ncorners); + P4EST_ASSERT (0 <= ddl && ddl < ecount); fprintf (cont->vtufile, -#ifdef P4EST_VTK_DOUBLES +#ifdef P4EST_ENABLE_VTK_DOUBLES " %24.16e\n", #else " %16.8e\n", @@ -1487,10 +1908,10 @@ p4est_vtk_write_point (p4est_vtk_context_t * cont, else { for (il = 0; il < Npoints; ++il) { ddl = use_nodes ? ntc[il] : il; - P4EST_ASSERT (0 <= ddl && ddl < Ncorners); + P4EST_ASSERT (0 <= ddl && ddl < ecount); fprintf (cont->vtufile, -#ifdef P4EST_VTK_DOUBLES +#ifdef P4EST_ENABLE_VTK_DOUBLES " %24.16e %24.16e %24.16e\n", #else " %16.8e %16.8e %16.8e\n", @@ -1505,7 +1926,7 @@ p4est_vtk_write_point (p4est_vtk_context_t * cont, float_data = P4EST_ALLOC (P4EST_VTK_FLOAT_TYPE, Npoints); for (il = 0; il < Npoints; ++il) { ddl = use_nodes ? ntc[il] : il; - P4EST_ASSERT (0 <= ddl && ddl < Ncorners); + P4EST_ASSERT (0 <= ddl && ddl < ecount); float_data[il] = (P4EST_VTK_FLOAT_TYPE) * ((double *) sc_array_index (values, ddl)); } @@ -1514,7 +1935,7 @@ p4est_vtk_write_point (p4est_vtk_context_t * cont, float_data = P4EST_ALLOC (P4EST_VTK_FLOAT_TYPE, Npoints * 3); for (il = 0; il < Npoints; ++il) { ddl = use_nodes ? ntc[il] : il; - P4EST_ASSERT (0 <= ddl && ddl < Ncorners); + P4EST_ASSERT (0 <= ddl && ddl < ecount); float_data[3 * il] = (P4EST_VTK_FLOAT_TYPE) * ((double *) sc_array_index (values, 3 * ddl)); @@ -1596,7 +2017,7 @@ p4est_vtk_write_cell (p4est_vtk_context_t * cont, if (!is_vector) { for (il = 0; il < Ncells; ++il) { fprintf (cont->vtufile, -#ifdef P4EST_VTK_DOUBLES +#ifdef P4EST_ENABLE_VTK_DOUBLES " %24.16e\n", #else " %16.8e\n", @@ -1608,7 +2029,7 @@ p4est_vtk_write_cell (p4est_vtk_context_t * cont, /* Write vector data */ for (il = 0; il < Ncells; ++il) { fprintf (cont->vtufile, -#ifdef P4EST_VTK_DOUBLES +#ifdef P4EST_ENABLE_VTK_DOUBLES " %24.16e %24.16e %24.16e\n", #else " %16.8e %16.8e %16.8e\n", @@ -1715,8 +2136,8 @@ p4est_vtk_write_footer (p4est_vtk_context_t * cont) * temporary copy. */ snprintf (filename_cpy, BUFSIZ, "%s", cont->filename); #ifdef _MSC_VER - _splitpath (filename_cpy, NULL, NULL, NULL, NULL); - filename_basename = filename_cpy; + _splitpath (filename_cpy, NULL, NULL, NULL, NULL); + filename_basename = filename_cpy; #else filename_basename = basename (filename_cpy); #endif diff --git a/src/p4est_vtk.h b/src/p4est_vtk.h index b291945bb..7aab211bd 100644 --- a/src/p4est_vtk.h +++ b/src/p4est_vtk.h @@ -50,8 +50,8 @@ typedef struct p4est_vtk_context p4est_vtk_context_t; * This function will abort if there is a file error. * * \param [in] p4est The p4est to be written. - * \param [in] geom A p4est_geometry_t structure or NULL for vertex space - * as defined by p4est->connectivity. + * \param [in] geom A \ref p4est_geometry_t structure or NULL for vertex space + * as defined by the \a p4est's \ref p4est_connectivity_t member. * \param [in] filename The first part of the file name which will have the * MPI rank appended to it: The output file will be * filename_rank.vtu, and the meta file filename.pvtu. @@ -164,6 +164,30 @@ void p4est_vtk_context_destroy (p4est_vtk_context_t * context); */ p4est_vtk_context_t *p4est_vtk_write_header (p4est_vtk_context_t * cont); +/** Write the VTK header for higher order visualization. + * + * This function follows the same routines as p4est_vtk_write_header. + * In addition, the caller must pass in an array containing coordinates for + * each point, as well as an integer representing the number of points in + * one direction each element has (for example, in an 8x8x8 cell, pass in 8). + * + * \param [in,out] cont A VTK context created by \ref p4est_vtk_context_new. + * None of the vtk_write functions must have been called. + * This context is the return value if no error occurs. + * \param [in] positions An sc_array_t of doubles containing the coordinates + * of all points to be written. Ordering of data is + * [ x_0, y_0, (z_0) ... x_n, y_n, (z_n) ] + * \param [in] Nnodes1D Integer number of points in each element in 1D. + * + * \return On success, an opaque context (p4est_vtk_context_t) pointer + * that must be passed to subsequent p4est_vtk calls. It is + * required to call \ref p4est_vtk_write_footer eventually with + * this value. Returns NULL on error. + */ +p4est_vtk_context_t *p4est_vtk_write_header_ho (p4est_vtk_context_t * cont, + sc_array_t * positions, + int Nnodes1D); + /** Write VTK cell data. * * There are options to have this function write diff --git a/src/p4est_wrap.c b/src/p4est_wrap.c index a416548c4..0697fc830 100644 --- a/src/p4est_wrap.c +++ b/src/p4est_wrap.c @@ -40,7 +40,7 @@ refine_callback (p4est_t * p4est, p4est_topidx_t which_tree, const p4est_locidx_t old_counter = pp->inside_counter++; const uint8_t flag = pp->flags[old_counter]; - P4EST_ASSERT (pp->coarsen_delay >= 0); + P4EST_ASSERT (pp->params.coarsen_delay >= 0); P4EST_ASSERT (0 <= old_counter); P4EST_ASSERT (0 <= pp->num_replaced && pp->num_replaced <= pp->num_refine_flags); @@ -52,7 +52,7 @@ refine_callback (p4est_t * p4est, p4est_topidx_t which_tree, /* increase quadrant's counter of most recent adaptation */ /* if refinement actually occurs, it will be reset to zero in all children */ - if (pp->coarsen_delay && q->p.user_int >= 0) { + if (pp->params.coarsen_delay && q->p.user_int >= 0) { ++q->p.user_int; } @@ -80,16 +80,16 @@ replace_on_refine (p4est_t * p4est, p4est_topidx_t which_tree, } /* reset the counter for most recent adaptation */ - P4EST_ASSERT (pp->coarsen_delay >= 0); - if (pp->coarsen_delay) { + P4EST_ASSERT (pp->params.coarsen_delay >= 0); + if (pp->params.coarsen_delay) { for (k = 0; k < P4EST_CHILDREN; ++k) { incoming[k]->p.user_int = 0; } } /* pass the replaced quadrants to the user-provided function */ - if (pp->replace_fn != NULL) { - pp->replace_fn (p4est, which_tree, + if (pp->params.replace_fn != NULL) { + pp->params.replace_fn (p4est, which_tree, num_outgoing, outgoing, num_incoming, incoming); } } @@ -102,7 +102,7 @@ coarsen_callback (p4est_t * p4est, p4est_topidx_t which_tree, const p4est_locidx_t old_counter = pp->inside_counter++; int k; - P4EST_ASSERT (pp->coarsen_delay >= 0); + P4EST_ASSERT (pp->params.coarsen_delay >= 0); /* are we not coarsening at all, just counting? */ if (q[1] == NULL) { @@ -115,8 +115,8 @@ coarsen_callback (p4est_t * p4est, p4est_topidx_t which_tree, /* coarsening flag was not set */ return 0; } - if (pp->coarsen_delay && q[k]->p.user_int >= 0 && - q[k]->p.user_int <= pp->coarsen_delay) { + if (pp->params.coarsen_delay && q[k]->p.user_int >= 0 && + q[k]->p.user_int <= pp->params.coarsen_delay) { /* most recent adaptation has been too recent */ return 0; } @@ -135,14 +135,14 @@ replace_on_coarsen (p4est_t * p4est, p4est_topidx_t which_tree, { p4est_wrap_t *pp = (p4est_wrap_t *) p4est->user_pointer; P4EST_ASSERT (num_incoming == 1 && num_outgoing == P4EST_CHILDREN); - P4EST_ASSERT (pp->coarsen_delay > 0); + P4EST_ASSERT (pp->params.coarsen_delay > 0); /* reset most recent adaptation timer */ - incoming[0]->p.user_int = pp->coarsen_affect ? 0 : -1; + incoming[0]->p.user_int = pp->params.coarsen_affect ? 0 : -1; /* pass the replaced quadrants to the user-provided function */ - if (pp->replace_fn != NULL) { - pp->replace_fn (p4est, which_tree, + if (pp->params.replace_fn != NULL) { + pp->params.replace_fn (p4est, which_tree, num_outgoing, outgoing, num_incoming, incoming); } } @@ -157,7 +157,7 @@ replace_on_balance (p4est_t * p4est, p4est_topidx_t which_tree, /* this function is called when refinement occurs in balance */ P4EST_ASSERT (num_outgoing == 1 && num_incoming == P4EST_CHILDREN); - P4EST_ASSERT (pp->coarsen_delay > 0); + P4EST_ASSERT (pp->params.coarsen_delay > 0); /* negative value means coarsening is allowed next time */ for (k = 0; k < P4EST_CHILDREN; ++k) { @@ -165,23 +165,60 @@ replace_on_balance (p4est_t * p4est, p4est_topidx_t which_tree, } /* pass the replaced quadrants to the user-provided function */ - if (pp->replace_fn != NULL) { - pp->replace_fn (p4est, which_tree, - num_outgoing, outgoing, num_incoming, incoming); + if (pp->params.replace_fn != NULL) { + pp->params.replace_fn (p4est, which_tree, + num_outgoing, outgoing, num_incoming, incoming); } } +void +p4est_wrap_params_init (p4est_wrap_params_t *params) +{ + memset (params, 0, sizeof (p4est_wrap_params_t)); + + params->hollow = 1; + p4est_mesh_params_init (¶ms->mesh_params); + params->replace_fn = NULL; + params->coarsen_delay = 0; + params->coarsen_affect = 0; + params->partition_for_coarsening = 1; + params->user_pointer = NULL; +} + p4est_wrap_t * p4est_wrap_new_conn (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn, int initial_level) { - return p4est_wrap_new_ext (mpicomm, conn, initial_level, - 0, P4EST_CONNECT_FULL, NULL, NULL); + p4est_wrap_params_t params; + + p4est_wrap_params_init (¶ms); + params.hollow = 0; + params.mesh_params.btype = P4EST_CONNECT_FULL; + params.mesh_params.compute_level_lists = 1; + params.mesh_params.compute_tree_index = 1; + + return p4est_wrap_new_params (mpicomm, conn, initial_level, ¶ms); } p4est_wrap_t * p4est_wrap_new_p4est (p4est_t * p4est, int hollow, p4est_connect_type_t btype, p4est_replace_t replace_fn, void *user_pointer) +{ + p4est_wrap_params_t params; + + p4est_wrap_params_init (¶ms); + params.hollow = hollow; + params.mesh_params.btype = btype; + params.mesh_params.compute_level_lists = 1; + params.mesh_params.compute_tree_index = 1; + params.replace_fn = replace_fn; + params.user_pointer = user_pointer; + + return p4est_wrap_new_p4est_params (p4est, ¶ms); +} + +p4est_wrap_t * +p4est_wrap_new_p4est_params (p4est_t * p4est, p4est_wrap_params_t * params) { p4est_wrap_t *pp; @@ -190,7 +227,14 @@ p4est_wrap_new_p4est (p4est_t * p4est, int hollow, p4est_connect_type_t btype, pp = P4EST_ALLOC_ZERO (p4est_wrap_t, 1); - pp->hollow = hollow; + /* store wrap parameters in wrap */ + if (params != NULL) { + pp->params = *params; + params = NULL; + } + else { + p4est_wrap_params_init (&pp->params); + } sc_refcount_init (&pp->conn_rc, p4est_package_id); pp->conn = p4est->connectivity; @@ -200,19 +244,20 @@ p4est_wrap_new_p4est (p4est_t * p4est, int hollow, p4est_connect_type_t btype, pp->p4est_half = P4EST_HALF; pp->p4est_faces = P4EST_FACES; pp->p4est_children = P4EST_CHILDREN; - pp->btype = btype; - pp->replace_fn = replace_fn; pp->p4est = p4est; pp->weight_exponent = 0; /* keep this even though using ALLOC_ZERO */ - if (!pp->hollow) { + if (!pp->params.hollow) { pp->flags = P4EST_ALLOC_ZERO (uint8_t, pp->p4est->local_num_quadrants); - pp->ghost = p4est_ghost_new (pp->p4est, pp->btype); - pp->mesh = p4est_mesh_new_ext (pp->p4est, pp->ghost, 1, 1, pp->btype); + pp->ghost = p4est_ghost_new (pp->p4est, pp->params.mesh_params.btype); + pp->mesh = + p4est_mesh_new_params (pp->p4est, pp->ghost, &pp->params.mesh_params); } + /* reset the data size since changing the p4est_wrap will affect p.user_int */ + p4est_reset_data (pp->p4est, 0, NULL, NULL); + pp->p4est->user_pointer = pp; - pp->user_pointer = user_pointer; return pp; } @@ -222,12 +267,27 @@ p4est_wrap_new_ext (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn, int initial_level, int hollow, p4est_connect_type_t btype, p4est_replace_t replace_fn, void *user_pointer) { - P4EST_ASSERT (p4est_connectivity_is_valid (conn)); + p4est_wrap_params_t params; + + p4est_wrap_params_init (¶ms); + params.hollow = hollow; + params.mesh_params.btype = btype; + params.mesh_params.compute_level_lists = 1; + params.mesh_params.compute_tree_index = 1; + params.replace_fn = replace_fn; + params.user_pointer = user_pointer; - return p4est_wrap_new_p4est (p4est_new_ext (mpicomm, conn, - 0, initial_level, 1, - 0, NULL, NULL), - hollow, btype, replace_fn, user_pointer); + return p4est_wrap_new_params (mpicomm, conn, initial_level, ¶ms); +} + +p4est_wrap_t * +p4est_wrap_new_params (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn, + int initial_level, p4est_wrap_params_t * params) +{ + P4EST_ASSERT (p4est_connectivity_is_valid (conn)); + return p4est_wrap_new_p4est_params (p4est_new_ext (mpicomm, conn, + 0, initial_level, 1, + 0, NULL, NULL), params); } p4est_wrap_t * @@ -240,7 +300,9 @@ p4est_wrap_new_copy (p4est_wrap_t * source, size_t data_size, pp = P4EST_ALLOC_ZERO (p4est_wrap_t, 1); - pp->hollow = 1; + /* copy the sources wrap paramters; however the copy will is hollow */ + pp->params = source->params; + pp->params.hollow = 1; sc_refcount_init_invalid (&pp->conn_rc); pp->conn_owner = (source->conn_owner != NULL ? source->conn_owner : source); @@ -251,8 +313,7 @@ p4est_wrap_new_copy (p4est_wrap_t * source, size_t data_size, pp->p4est_half = P4EST_HALF; pp->p4est_faces = P4EST_FACES; pp->p4est_children = P4EST_CHILDREN; - pp->btype = source->btype; - pp->replace_fn = replace_fn; + pp->params.replace_fn = replace_fn; pp->p4est = p4est_copy (source->p4est, 0); if (data_size > 0) { p4est_reset_data (pp->p4est, data_size, NULL, NULL); @@ -261,7 +322,7 @@ p4est_wrap_new_copy (p4est_wrap_t * source, size_t data_size, pp->weight_exponent = 0; /* keep this even though using ALLOC_ZERO */ pp->p4est->user_pointer = pp; - pp->user_pointer = user_pointer; + pp->params.user_pointer = user_pointer; return pp; } @@ -396,7 +457,7 @@ p4est_wrap_destroy (p4est_wrap_t * pp) p4est_ghost_destroy (pp->ghost_aux); } - if (!pp->hollow) { + if (!pp->params.hollow) { p4est_mesh_destroy (pp->mesh); p4est_ghost_destroy (pp->ghost); } @@ -425,7 +486,7 @@ void p4est_wrap_set_hollow (p4est_wrap_t * pp, int hollow) { /* Verify consistency */ - if (!pp->hollow) { + if (!pp->params.hollow) { P4EST_ASSERT (pp->flags != NULL); P4EST_ASSERT (pp->ghost != NULL); P4EST_ASSERT (pp->mesh != NULL); @@ -443,15 +504,16 @@ p4est_wrap_set_hollow (p4est_wrap_t * pp, int hollow) P4EST_ASSERT (pp->mesh_aux == NULL); /* Do nothing if the status is right */ - if (hollow == pp->hollow) { + if (hollow == pp->params.hollow) { return; } - if (pp->hollow) { + if (pp->params.hollow) { /* Allocate the ghost, mesh, and flag members */ pp->flags = P4EST_ALLOC_ZERO (uint8_t, pp->p4est->local_num_quadrants); - pp->ghost = p4est_ghost_new (pp->p4est, pp->btype); - pp->mesh = p4est_mesh_new_ext (pp->p4est, pp->ghost, 1, 1, pp->btype); + pp->ghost = p4est_ghost_new (pp->p4est, pp->params.mesh_params.btype); + pp->mesh = + p4est_mesh_new_params (pp->p4est, pp->ghost, &pp->params.mesh_params); } else { /* Free and nullify the ghost, mesh, and flag members */ @@ -463,7 +525,7 @@ p4est_wrap_set_hollow (p4est_wrap_t * pp, int hollow) pp->flags = NULL; } pp->num_refine_flags = pp->inside_counter = pp->num_replaced = 0; - pp->hollow = hollow; + pp->params.hollow = hollow; } void @@ -480,8 +542,8 @@ p4est_wrap_set_coarsen_delay (p4est_wrap_t * pp, P4EST_ASSERT (pp != NULL); P4EST_ASSERT (coarsen_delay >= 0); - pp->coarsen_delay = coarsen_delay; - pp->coarsen_affect = coarsen_affect; + pp->params.coarsen_delay = coarsen_delay; + pp->params.coarsen_affect = coarsen_affect; p4est = pp->p4est; P4EST_ASSERT (p4est->data_size == 0); @@ -496,10 +558,17 @@ p4est_wrap_set_coarsen_delay (p4est_wrap_t * pp, } } +void +p4est_wrap_set_partitioning (p4est_wrap_t *pp, int partition_for_coarsening) +{ + P4EST_ASSERT (pp != NULL); + pp->params.partition_for_coarsening = partition_for_coarsening; +} + p4est_ghost_t * p4est_wrap_get_ghost (p4est_wrap_t * pp) { - P4EST_ASSERT (!pp->hollow); + P4EST_ASSERT (!pp->params.hollow); return pp->match_aux ? pp->ghost_aux : pp->ghost; } @@ -507,7 +576,7 @@ p4est_wrap_get_ghost (p4est_wrap_t * pp) p4est_mesh_t * p4est_wrap_get_mesh (p4est_wrap_t * pp) { - P4EST_ASSERT (!pp->hollow); + P4EST_ASSERT (!pp->params.hollow); return pp->match_aux ? pp->mesh_aux : pp->mesh; } @@ -521,7 +590,7 @@ p4est_wrap_mark_refine (p4est_wrap_t * pp, p4est_locidx_t pos; uint8_t flag; - P4EST_ASSERT (!pp->hollow); + P4EST_ASSERT (!pp->params.hollow); P4EST_ASSERT (p4est->first_local_tree <= which_tree); P4EST_ASSERT (which_tree <= p4est->last_local_tree); @@ -548,7 +617,7 @@ p4est_wrap_mark_coarsen (p4est_wrap_t * pp, p4est_locidx_t pos; uint8_t flag; - P4EST_ASSERT (!pp->hollow); + P4EST_ASSERT (!pp->params.hollow); P4EST_ASSERT (p4est->first_local_tree <= which_tree); P4EST_ASSERT (which_tree <= p4est->last_local_tree); @@ -570,14 +639,16 @@ int p4est_wrap_adapt (p4est_wrap_t * pp) { int changed; + int have_zlib; #ifdef P4EST_ENABLE_DEBUG p4est_locidx_t jl, local_num; #endif - p4est_gloidx_t global_num; + p4est_gloidx_t global_num, global_num_entry; + unsigned checksum_entry, checksum_exit; p4est_t *p4est = pp->p4est; - P4EST_ASSERT (!pp->hollow); - P4EST_ASSERT (pp->coarsen_delay >= 0); + P4EST_ASSERT (!pp->params.hollow); + P4EST_ASSERT (pp->params.coarsen_delay >= 0); P4EST_ASSERT (pp->mesh != NULL); P4EST_ASSERT (pp->ghost != NULL); @@ -594,6 +665,13 @@ p4est_wrap_adapt (p4est_wrap_t * pp) (P4EST_CHILDREN - 1) * pp->num_refine_flags); + + if ((have_zlib = p4est_have_zlib())) { + /* store p4est checksum on entry to compare with results after balancing */ + global_num_entry = p4est->global_num_quadrants; + checksum_entry = p4est_checksum (p4est); + } + /* Execute refinement */ pp->inside_counter = pp->num_replaced = 0; #ifdef P4EST_ENABLE_DEBUG @@ -613,7 +691,8 @@ p4est_wrap_adapt (p4est_wrap_t * pp) #endif global_num = p4est->global_num_quadrants; p4est_coarsen_ext (p4est, 0, 1, coarsen_callback, NULL, - pp->coarsen_delay ? replace_on_coarsen : pp->replace_fn); + pp->params.coarsen_delay ? replace_on_coarsen : + pp->params.replace_fn); P4EST_ASSERT (pp->inside_counter == local_num); P4EST_ASSERT (local_num - p4est->local_num_quadrants == pp->num_replaced * (P4EST_CHILDREN - 1)); @@ -625,14 +704,31 @@ p4est_wrap_adapt (p4est_wrap_t * pp) /* Only if refinement and/or coarsening happened do we need to balance */ if (changed) { - P4EST_FREE (pp->flags); - p4est_balance_ext (p4est, pp->btype, NULL, pp->coarsen_delay ? - replace_on_balance : pp->replace_fn); - pp->flags = P4EST_ALLOC_ZERO (uint8_t, p4est->local_num_quadrants); + p4est_balance_ext (p4est, pp->params.mesh_params.btype, NULL, + pp->params.coarsen_delay ? replace_on_balance : + pp->params.replace_fn); + + /* check if coarsening and balancing canceled out */ + if (have_zlib && (global_num_entry == p4est->global_num_quadrants)) { + /* only compute another checksum for unchanged global quadrant counts */ + checksum_exit = p4est_checksum (p4est); + changed = (checksum_entry != checksum_exit); + } - pp->ghost_aux = p4est_ghost_new (p4est, pp->btype); - pp->mesh_aux = p4est_mesh_new_ext (p4est, pp->ghost_aux, 1, 1, pp->btype); - pp->match_aux = 1; + if (changed) { + /* compute new ghost and mesh for the changed p4est */ + P4EST_FREE (pp->flags); + pp->flags = P4EST_ALLOC_ZERO (uint8_t, p4est->local_num_quadrants); + + pp->ghost_aux = p4est_ghost_new (p4est, pp->params.mesh_params.btype); + pp->mesh_aux = + p4est_mesh_new_params (p4est, pp->ghost_aux, &pp->params.mesh_params); + pp->match_aux = 1; + } + else { + memset (pp->flags, 0, + sizeof (uint8_t) * pp->p4est->local_num_quadrants); + } } #ifdef P4EST_ENABLE_DEBUG else { @@ -714,7 +810,7 @@ p4est_wrap_partition (p4est_wrap_t * pp, int weight_exponent, p4est_gloidx_t pre_me, pre_next; p4est_gloidx_t post_me, post_next; - P4EST_ASSERT (!pp->hollow); + P4EST_ASSERT (!pp->params.hollow); P4EST_ASSERT (pp->ghost != NULL); P4EST_ASSERT (pp->mesh != NULL); @@ -746,15 +842,16 @@ p4est_wrap_partition (p4est_wrap_t * pp, int weight_exponent, P4EST_ASSERT (weight_exponent == 0 || weight_exponent == 1); pp->weight_exponent = weight_exponent; changed = - p4est_partition_ext (pp->p4est, 1, + p4est_partition_ext (pp->p4est, pp->params.partition_for_coarsening, weight_exponent ? partition_weight : NULL) > 0; if (changed) { P4EST_FREE (pp->flags); pp->flags = P4EST_ALLOC_ZERO (uint8_t, pp->p4est->local_num_quadrants); - pp->ghost = p4est_ghost_new (pp->p4est, pp->btype); - pp->mesh = p4est_mesh_new_ext (pp->p4est, pp->ghost, 1, 1, pp->btype); + pp->ghost = p4est_ghost_new (pp->p4est, pp->params.mesh_params.btype); + pp->mesh = + p4est_mesh_new_params (pp->p4est, pp->ghost, &pp->params.mesh_params); /* Query the window onto global quadrant sequence after partition */ if (unchanged_first != NULL || unchanged_length != NULL || @@ -785,7 +882,7 @@ p4est_wrap_partition (p4est_wrap_t * pp, int weight_exponent, void p4est_wrap_complete (p4est_wrap_t * pp) { - P4EST_ASSERT (!pp->hollow); + P4EST_ASSERT (!pp->params.hollow); P4EST_ASSERT (pp->ghost != NULL); P4EST_ASSERT (pp->mesh != NULL); diff --git a/src/p4est_wrap.h b/src/p4est_wrap.h index 4c2d9db02..9e4bd0f76 100644 --- a/src/p4est_wrap.h +++ b/src/p4est_wrap.h @@ -32,7 +32,6 @@ * quadrants, respectively, which can help make application code cleaner. */ -#include #include #include @@ -48,22 +47,44 @@ typedef enum p4est_wrap_flags } p4est_wrap_flags_t; -typedef struct p4est_wrap +/** This structure contains the different parameters of wrap creation. + * A default instance can be initialized by calling \ref p4est_wrap_params_init + * and used for wrap creation by calling \ref p4est_wrap_new_params. */ +typedef struct { - /* this member is never used or changed by p4est_wrap */ - void *user_pointer; /**< Convenience member for users */ - - /** If true, this wrap has NULL for ghost, mesh, and flag members. - * If false, they are properly allocated and kept current internally. */ - int hollow; - - /** Non-negative integer tells us how many adaptations to wait - * before any given quadrant may be coarsened again. */ - int coarsen_delay; + int hollow; /**< Do not allocate flags, ghost, and + mesh members. */ + p4est_mesh_params_t mesh_params; /**< Parameters for mesh creation. The + btype member is used for ghost + creation as well. */ + p4est_replace_t replace_fn; /**< This member may be removed soon. + Callback to replace quadrants during + refinement, coarsening or balancing + in \ref p4est_wrap_adapt. May be NULL. + The callback should not change the + p4est's user data. */ + int coarsen_delay; /**< Non-negative integer telling how + many adaptations to wait before any + given quadrant may be coarsened + again. */ + int coarsen_affect; /**< Boolean: If true, we delay + coarsening not only after refinement, + but also between subsequent + coarsenings of the same quadrant. */ + int partition_for_coarsening; /**< If true, the partition is + modified to allow one level + of coarsening when calling + \ref p4est_wrap_partition. */ + void *user_pointer; /**< Set the user pointer in + \ref p4est_wrap_t. Subsequently, we + will never access it. */ +} +p4est_wrap_params_t; - /** Boolean: If true, we delay coarsening not only after refinement, - * but also between subsequent coarsenings of the same quadrant. */ - int coarsen_affect; +typedef struct p4est_wrap +{ + /* collection of wrap-related parameters */ + p4est_wrap_params_t params; /** This reference counter is a workaround for internal use only. * Until we have refcounting/copy-on-write for the connectivity, @@ -79,8 +100,6 @@ typedef struct p4est_wrap int p4est_half; int p4est_faces; int p4est_children; - p4est_connect_type_t btype; - p4est_replace_t replace_fn; p4est_t *p4est; /**< p4est->user_pointer is used internally */ /* anything below here is considered private und should not be touched */ @@ -97,9 +116,15 @@ typedef struct p4est_wrap } p4est_wrap_t; +/** Initialize a default \ref p4est_wrap_params_t structure. + * The parameters are set to create the most basic, hollow wrap structure. */ +void p4est_wrap_params_init (p4est_wrap_params_t * params); + /** Create a p4est wrapper from a given connectivity structure. * The ghost and mesh members are initialized as well as the flags. * The btype is set to P4EST_CONNECT_FULL. + * This function sets a subset of the wrap creation parameters. For full control + * use \ref p4est_wrap_new_params. * \param [in] mpicomm We expect sc_MPI_Init to be called already. * \param [in] conn Connectivity structure. Wrap takes ownership. * \param [in] initial_level Initial level of uniform refinement. @@ -113,6 +138,8 @@ p4est_wrap_t *p4est_wrap_new_conn (sc_MPI_Comm mpicomm, * \param [in,out] p4est Valid p4est object that we will own. * We take ownership of its connectivity too. * Its user pointer must be NULL and will be changed. + * Its data size will be set to 0 and the quadrant + * data will be freed. * \param [in] hollow Do not allocate flags, ghost, and mesh members. * \param [in] btype The neighborhood used for balance, ghost, mesh. * \param [in] replace_fn Callback to replace quadrants during refinement, @@ -127,8 +154,26 @@ p4est_wrap_t *p4est_wrap_new_p4est (p4est_t * p4est, int hollow, p4est_replace_t replace_fn, void *user_pointer); +/** Create a wrapper for a given p4est structure. + * Like \ref p4est_wrap_new_p4est, but with \a params to completely control the + * wrap creation process. + * \param [in,out] p4est Valid p4est object that we will own. + * We take ownership of its connectivity too. + * Its user pointer must be NULL and will be changed. + * Its data size will be set to 0 and the quadrant + * data will be freed. + * \param [in] params The wrap creation parameters. If NULL, the function + * defaults to the parameters of + * \ref p4est_wrap_params_init. + * \return A fully initialized p4est_wrap structure. + */ +p4est_wrap_t *p4est_wrap_new_p4est_params (p4est_t * p4est, + p4est_wrap_params_t * params); + /** Create a p4est wrapper from a given connectivity structure. - * Like p4est_wrap_new_conn, but with extra parameters \a hollow and \a btype. + * Like \ref p4est_wrap_new_conn, but with extra parameters \a hollow and \a btype. + * This function sets a subset of the wrap creation parameters. For full control + * use \ref p4est_wrap_new_params. * \param [in] mpicomm We expect sc_MPI_Init to be called already. * \param [in] conn Connectivity structure. Wrap takes ownership. * \param [in] initial_level Initial level of uniform refinement. @@ -149,6 +194,23 @@ p4est_wrap_t *p4est_wrap_new_ext (sc_MPI_Comm mpicomm, p4est_replace_t replace_fn, void *user_pointer); +/** Create a p4est wrapper from a given connectivity structure. + * Like \ref p4est_wrap_new_conn, but with \a params to completely control the + * wrap creation process. + * \param [in] mpicomm We expect sc_MPI_Init to be called already. + * \param [in] conn Connectivity structure. Wrap takes ownership. + * \param [in] initial_level Initial level of uniform refinement. + * No effect if less/equal to zero. + * \param [in] params The wrap creation parameters. If NULL, the function + * defaults to the parameters of + * \ref p4est_wrap_params_init. + * \return A fully initialized p4est_wrap structure. + */ +p4est_wrap_t *p4est_wrap_new_params (sc_MPI_Comm mpicomm, + p4est_connectivity_t * conn, + int initial_level, + p4est_wrap_params_t * params); + /** Create a p4est wrapper from an existing one. * \note This wrapper must be destroyed before the original one. * We set it to hollow and copy the original p4est data structure. @@ -223,6 +285,21 @@ void p4est_wrap_set_coarsen_delay (p4est_wrap_t * pp, int coarsen_delay, int coarsen_affect); +/** Set a parameter that ensures future partitions allow one level of coarsening. + * The partition_for_coarsening parameter is passed to \ref p4est_partition_ext + * in \ref p4est_wrap_partition. + * If not zero, all future calls to \ref p4est_wrap_partition will partition + * in a manner that allows one level of coarsening. This function does not + * automatically repartition the mesh, when switching partition_for_coarsening + * to a non-zero value. + * \param [in,out] pp A valid p4est_wrap structure. + * \param [in] partition_for_coarsening Boolean: If true, all future partitions + * of the wrap allow one level of coarsening. + * Suggested default: 1. + */ +void p4est_wrap_set_partitioning (p4est_wrap_t *pp, + int partition_for_coarsening); + /** Return the appropriate ghost layer. * This function is necessary since two versions may exist simultaneously * after refinement and before partition/complete. @@ -261,6 +338,8 @@ void p4est_wrap_mark_coarsen (p4est_wrap_t * pp, * Checks pp->flags as per-quadrant input against p4est_wrap_flags_t. * The pp->flags array is updated along with p4est and reset to zeros. * Creates ghost_aux and mesh_aux to represent the intermediate mesh. + * If zlib is available, the routine checks whether coarsening and balancing the + * p4est canceled out and skips computing ghost_aux and mesh_aux when possible. * \param [in,out] pp The p4est wrapper to work with, must not be hollow. * \return boolean whether p4est has changed. * If true, partition must be called. diff --git a/src/p6est.c b/src/p6est.c index 3d92cfe2c..4a77fd3fa 100644 --- a/src/p6est.c +++ b/src/p6est.c @@ -50,6 +50,12 @@ #include #endif +static sc_mempool_t * +p2est_quadrant_mempool_new (void) +{ + return sc_mempool_new_zero_and_persist (sizeof (p2est_quadrant_t)); +} + p6est_connectivity_t * p6est_connectivity_new (p4est_connectivity_t * conn4, double *top_vertices, double height[3]) @@ -162,8 +168,8 @@ p6est_connectivity_memory_used (p6est_connectivity_t * conn) { return p4est_connectivity_memory_used (conn->conn4) + - conn->top_vertices == NULL ? 0 : - (conn->conn4->num_vertices * 3 * sizeof (double)); + ((conn->top_vertices == NULL) ? 0 : + (conn->conn4->num_vertices * 3 * sizeof (double))); } int @@ -289,7 +295,7 @@ p6est_connectivity_save (const char *filename, p6est_connectivity_t * conn) } p6est_connectivity_t * -p6est_connectivity_load (const char *filename, size_t * bytes) +p6est_connectivity_load (const char *filename, size_t *bytes) { int retval; size_t bytes_in; @@ -410,7 +416,7 @@ p6est_new_ext (sc_MPI_Comm mpicomm, p6est_connectivity_t * connectivity, user_data_pool = NULL; } - p6est->layer_pool = sc_mempool_new (sizeof (p2est_quadrant_t)); + p6est->layer_pool = p2est_quadrant_mempool_new (); p6est->data_size = data_size; p6est->user_pointer = user_pointer; @@ -501,7 +507,7 @@ p6est_new_from_p4est (p4est_t * p4est, double *top_vertices, double height[3], conn = p6est_connectivity_new (p4est->connectivity, top_vertices, height); - p6est->layer_pool = sc_mempool_new (sizeof (p2est_quadrant_t)); + p6est->layer_pool = p2est_quadrant_mempool_new (); p6est->data_size = data_size; p6est->user_pointer = user_pointer; @@ -603,7 +609,7 @@ p6est_copy_ext (p6est_t * input, int copy_data, int duplicate_comm) else { p6est->data_size = 0; } - p6est->layer_pool = sc_mempool_new (sizeof (p2est_quadrant_t)); + p6est->layer_pool = p2est_quadrant_mempool_new (); if (p6est->data_size > 0) { P4EST_ASSERT (copy_data); @@ -761,7 +767,7 @@ p6est_save_ext (const char *filename, p6est_t * p6est, SC_CHECK_ABORT (file != NULL, "file open"); /* explicitly seek to end to avoid bad ftell return value on Windows */ - retval = fseek(file, 0, SEEK_END); + retval = fseek (file, 0, SEEK_END); SC_CHECK_ABORT (retval == 0, "file seek"); /* align */ @@ -1007,7 +1013,7 @@ p6est_load_ext (const char *filename, sc_MPI_Comm mpicomm, size_t data_size, p6est->mpisize + 1); p6est->layers = sc_array_new_size (sizeof (p2est_quadrant_t), (size_t) nlayers); - p6est->layer_pool = sc_mempool_new (sizeof (p2est_quadrant_t)); + p6est->layer_pool = p2est_quadrant_mempool_new (); p6est->user_pointer = user_pointer; p6est->user_data_pool = data_size ? sc_mempool_new (data_size) : NULL; @@ -1325,7 +1331,7 @@ p6est_refine_layers_ext (p6est_t * p6est, int refine_recursive, } else { /* parent is accepted */ - newq = (p2est_quadrant_t *) sc_array_push (newcol); + newq = p2est_quadrant_array_push (newcol); *newq = *parent; if (parent == &p) { parent = &nextq[level]; @@ -1600,7 +1606,7 @@ p6est_replace_column_join (p4est_t * p4est, p4est_topidx_t which_tree, P4EST_ASSERT (zw[j] < nlayers[j]); q[j] = p2est_quadrant_array_index (layers, first[j] + zw[j]); } - p = (p2est_quadrant_t *) sc_array_push (work_array); + p = p2est_quadrant_array_push (work_array); *p = *q[0]; p6est_layer_init_data (p6est, which_tree, incoming[0], p, init_fn); for (j = 1; j < num_outgoing; j++) { diff --git a/src/p6est.h b/src/p6est.h index 6e274081f..4aa580548 100644 --- a/src/p6est.h +++ b/src/p6est.h @@ -81,11 +81,11 @@ p6est_connectivity_t; * * \param[in] conn4 the 2D connectivity * \param[in] top_vertices if NULL, then the sheet has a uniform vertical - * profile; otherwise, \a top_vertices gives teh + * profile; otherwise, \a top_vertices gives the * vertices of the top of the sheet; should be the * same size as \a conn4->tree_to_vertex * \param[in] height if \a top_vertices == NULL, then this gives the - * offset fro the bottom of the sheet to the top. + * offset from the bottom of the sheet to the top. * * \return the 2D+1D connectivity information. */ @@ -174,7 +174,9 @@ typedef struct p6est never touched by p4est */ p6est_connectivity_t *connectivity; /**< topology of sheet, not owned. */ p4est_t *columns; /**< 2D description of column layout - built from \a connectivity */ + built from \a connectivity. + \note columns->p.user_data + cannot be used. */ sc_array_t *layers; /**< single array that stores p2est_quadrant_t layers within columns */ sc_mempool_t *user_data_pool; /**< memory allocator for user data */ @@ -330,6 +332,8 @@ p6est_t *p6est_new (sc_MPI_Comm mpicomm, * * \return This returns a valid forest. The user must destroy the * connectivity for the new p6est independently. + * + * \note p4est->p.user_data is not retained. */ p6est_t *p6est_new_from_p4est (p4est_t * p4est, double *top_vertices, @@ -559,9 +563,13 @@ p2est_quadrant_array_index (sc_array_t * array, size_t it) static inline p2est_quadrant_t * p2est_quadrant_array_push (sc_array_t * array) { + p2est_quadrant_t *q; + P4EST_ASSERT (array->elem_size == sizeof (p2est_quadrant_t)); - return (p2est_quadrant_t *) sc_array_push (array); + q = (p2est_quadrant_t *) sc_array_push (array); + P2EST_QUADRANT_INIT(q); + return q; } /** Call sc_mempool_alloc for a mempool creating quadrants. */ diff --git a/src/p6est_extended.h b/src/p6est_extended.h index 96712c8f2..2834299db 100644 --- a/src/p6est_extended.h +++ b/src/p6est_extended.h @@ -56,7 +56,7 @@ SC_EXTERN_C_BEGIN; * \param [in] min_zlevel The forest is vertically refined at least to * this level. May be negative or 0, then it has * no effect. - * \parem [in] num_zroot The number of "root" vertical layers + * \param [in] num_zroot The number of "root" vertical layers * (used when non-power-of-2 layers are desired) * \param [in] fill_uniform If true, fill the forest with a uniform mesh * instead of the coarsest possible one. diff --git a/src/p6est_lnodes.h b/src/p6est_lnodes.h index bd6bfd7ba..f257fae38 100644 --- a/src/p6est_lnodes.h +++ b/src/p6est_lnodes.h @@ -25,7 +25,6 @@ #ifndef P6EST_LNODES_H #define P6EST_LNODES_H -#include #include #include #include @@ -37,13 +36,12 @@ SC_EXTERN_C_BEGIN; * for the types of hanging faces that occur in a p6est. Please see the * documentation for p8est_lnodes */ -/* The only other differece is in the numbering of nodes and the number of - * faces. +/* The only other difference is in the numbering of nodes, edges, and faces. * * Columns of nodes are numbered contiguously: this still generates a * partition-unique numbering. * - * Although we call a p2est_quadrant_t coordinate layer->z, the orientaton of + * Although we call a p2est_quadrant_t coordinate layer->z, the orientation of * a layer from lnodes perspective is that the vertical axis is the X axis of * the 3D element, the x axis of the columns is the Y axis of the 3D element, * and the y axis of the columns is the Z axis of the 3D element diff --git a/src/p6est_profile.h b/src/p6est_profile.h index eb7314eb7..c5b7e4a09 100644 --- a/src/p6est_profile.h +++ b/src/p6est_profile.h @@ -25,9 +25,6 @@ #ifndef P6EST_PROFILE_H #define P6EST_PROFILE_H -#include -#include -#include #include SC_EXTERN_C_BEGIN; @@ -89,7 +86,7 @@ void p6est_profile_balance_local (p6est_profile_t * profile); * intersections, as determined at profile creation in \a * p6est_profile_new_local * - * \return whether any change has occured. + * \return whether any change has occurred. * */ int p6est_profile_sync (p6est_profile_t * profile); diff --git a/src/p6est_vtk.c b/src/p6est_vtk.c index 4fc3b29db..5634e567f 100644 --- a/src/p6est_vtk.c +++ b/src/p6est_vtk.c @@ -32,7 +32,7 @@ static const int p6est_vtk_write_tree = 1; static const int p6est_vtk_write_rank = 1; static const int p6est_vtk_wrap_rank = 0; -#ifndef P4EST_VTK_DOUBLES +#ifndef P4EST_ENABLE_VTK_DOUBLES #define P4EST_VTK_FLOAT_NAME "Float32" #define P4EST_VTK_FLOAT_TYPE float #else @@ -40,7 +40,7 @@ static const int p6est_vtk_wrap_rank = 0; #define P4EST_VTK_FLOAT_TYPE double #endif -#ifndef P4EST_VTK_BINARY +#ifndef P4EST_ENABLE_VTK_BINARY #define P4EST_VTK_ASCII 1 #define P4EST_VTK_FORMAT_STRING "ascii" #else @@ -50,14 +50,14 @@ static int p6est_vtk_write_binary (FILE * vtkfile, char *numeric_data, size_t byte_length) { -#ifndef P4EST_VTK_COMPRESSION +#ifndef P4EST_ENABLE_VTK_COMPRESSION return sc_vtk_write_binary (vtkfile, numeric_data, byte_length); #else return sc_vtk_write_compressed (vtkfile, numeric_data, byte_length); -#endif /* P4EST_VTK_COMPRESSION */ +#endif /* P4EST_ENABLE_VTK_COMPRESSION */ } -#endif /* P4EST_VTK_BINARY */ +#endif /* P4EST_ENABLE_VTK_BINARY */ void p6est_vtk_write_file (p6est_t * p6est, const char *filename) @@ -203,7 +203,7 @@ p6est_vtk_write_header (p6est_t * p6est, fprintf (vtufile, "\n"); fprintf (vtufile, "\n"); fprintf (pvtufile, "array + sizeof (p8est_quadrant_t) * it); } -/** Call sc_array_push for a quadrant array. */ +/** Push the copy of a fully initialized quadrant onto a quadrant array. + * \param [in,out] array Valid array of quadrants is pushed to. + * \param [in] qsrc Pointer to a quadrant with fully initialized memory. + * This means all bits in the quadrant mush have been + * written to at least once, even the compiler padding. + * This serves to make the function clean for valgrind. + * \return Newly allocated quadrant with contents of \a qsrc. + */ +static inline p8est_quadrant_t * +p8est_quadrant_array_push_copy (sc_array_t * array, + const p8est_quadrant_t *qsrc) +{ + p8est_quadrant_t *q; + + P4EST_ASSERT (array->elem_size == sizeof (p8est_quadrant_t)); + + q = (p8est_quadrant_t *) sc_array_push (array); + *q = *qsrc; + return q; +} + +/** Call sc_array_push for a quadrant array and fully initialize memory. + * \param [in,out] array Valid array of quadrants is pushed to. + * \return Pushed array element with fully initialized memory. + * In this case, we're writing to all bits of it. + * This serves to make the quadrant clean for valgrind. + */ /*@unused@*/ static inline p8est_quadrant_t * p8est_quadrant_array_push (sc_array_t * array) { + p8est_quadrant_t *q; + P4EST_ASSERT (array->elem_size == sizeof (p8est_quadrant_t)); - return (p8est_quadrant_t *) sc_array_push (array); + q = (p8est_quadrant_t *) sc_array_push (array); + P8EST_QUADRANT_INIT(q); + return q; } /** Call sc_mempool_alloc for a mempool creating quadrants. */ diff --git a/src/p8est_algorithms.h b/src/p8est_algorithms.h index 8d5dea3c5..05cf10ae7 100644 --- a/src/p8est_algorithms.h +++ b/src/p8est_algorithms.h @@ -33,11 +33,15 @@ #ifndef P8EST_ALGORITHMS_H #define P8EST_ALGORITHMS_H -#include #include SC_EXTERN_C_BEGIN; +/** Create a memory pool for quadrants that initializes compiler padding. + * \return Initialized mempool with zero_and_persist setting. + */ +sc_mempool_t *p8est_quadrant_mempool_new (void); + /** Alloc and initialize the user data of a valid quadrant. * \param [in] which_tree 0-based index of this quadrant's tree. * \param [in,out] quad The quadrant to be initialized. @@ -334,6 +338,22 @@ p4est_gloidx_t p8est_partition_given (p8est_t * p8est, const p4est_locidx_t * num_quadrants_in_proc); +/** Checks if a quadrant's face is on the boundary of the forest. + * + * \param [in] p8est The forest in which to search for \a q + * \param [in] treeid The tree to which \a q belongs. + * \param [in] q The quadrant that is in question. + * \param [in] face The face of the quadrant that is in question. + * + * \return true if the quadrant's face is on the boundary of the forest and + * false otherwise. + */ +int p8est_quadrant_on_face_boundary (p8est_t * p4est, + p4est_topidx_t treeid, + int face, + const p8est_quadrant_t * + q); + SC_EXTERN_C_END; #endif /* !P8EST_ALGORITHMS_H */ diff --git a/src/p8est_bits.c b/src/p8est_bits.c index f277b2928..eb2352955 100644 --- a/src/p8est_bits.c +++ b/src/p8est_bits.c @@ -182,8 +182,7 @@ p8est_quadrant_edge_neighbor_extra (const p4est_quadrant_t * q, p4est_topidx_t p8est_quadrant_edge_neighbor (q, edge, &temp); if (p4est_quadrant_is_inside_root (&temp)) { - qp = p4est_quadrant_array_push (quads); - *qp = temp; + qp = p4est_quadrant_array_push_copy (quads, &temp); tp = (p4est_topidx_t *) sc_array_push (treeids); *tp = t; if (nedges != NULL) { @@ -369,6 +368,60 @@ p8est_quadrant_touches_edge (const p4est_quadrant_t * q, int edge, int inside) return incount == 2; } +void +p8est_coordinates_transform_edge (const p4est_qcoord_t coords_in[], + p4est_qcoord_t coords_out[], + const p8est_edge_info_t * ei, + const p8est_edge_transform_t * et) +{ + int iaxis; + p4est_qcoord_t my_xyz, *target_xyz[3]; + + iaxis = (int) ei->iedge / 4; + P4EST_ASSERT (0 <= et->naxis[0] && et->naxis[0] < 3); + P4EST_ASSERT (0 <= et->naxis[1] && et->naxis[1] < 3); + P4EST_ASSERT (0 <= et->naxis[2] && et->naxis[2] < 3); + P4EST_ASSERT (et->naxis[0] != et->naxis[1] && + et->naxis[0] != et->naxis[2] && et->naxis[1] != et->naxis[2]); + P4EST_ASSERT (0 <= et->nflip && et->nflip < 2); + P4EST_ASSERT (coords_in != coords_out); + + for (int d = 0; d < P4EST_DIM; d++) { + target_xyz[d] = &coords_out[d]; + } + + /* transform coordinate axis parallel to edge */ + my_xyz = coords_in[iaxis]; + if (!et->nflip) { + *target_xyz[et->naxis[0]] = my_xyz; + } + else { + *target_xyz[et->naxis[0]] = P4EST_ROOT_LEN - my_xyz; + } + + /* create the other two coordinates */ + switch (et->corners) { + case 0: + *target_xyz[et->naxis[1]] = 0; + *target_xyz[et->naxis[2]] = 0; + break; + case 1: + *target_xyz[et->naxis[1]] = P4EST_ROOT_LEN; + *target_xyz[et->naxis[2]] = 0; + break; + case 2: + *target_xyz[et->naxis[1]] = 0; + *target_xyz[et->naxis[2]] = P4EST_ROOT_LEN; + break; + case 3: + *target_xyz[et->naxis[1]] = P4EST_ROOT_LEN; + *target_xyz[et->naxis[2]] = P4EST_ROOT_LEN; + break; + default: + SC_ABORT_NOT_REACHED (); + } +} + void p8est_quadrant_transform_edge (const p4est_quadrant_t * q, p4est_quadrant_t * r, diff --git a/src/p8est_bits.h b/src/p8est_bits.h index ebe6b214a..f946b0b37 100644 --- a/src/p8est_bits.h +++ b/src/p8est_bits.h @@ -88,11 +88,23 @@ int p8est_quadrant_is_equal_piggy (const p8est_quadrant_t * */ int p8est_quadrant_compare (const void *v1, const void *v2); +/** Compare two sets of coordinates in their Morton ordering. + * Coordinates are signed, but the sorted order will treat them + * as unsigned, with negative coordinates being greater than + * positive coordinates because of their representation in twos-complement. + * \param [in] v1, v2 Two sets of 3d coordinates. + * \return Returns < 0 if \a v1 < \a v2, + * 0 if \a v1 == \a v2, + * > 0 if \a v1 > \a v2 + */ +int p8est_coordinates_compare (const p4est_qcoord_t v1[], + const p4est_qcoord_t v2[]); + /** Compare two quadrants in their Morton ordering, with equivalence if the * two quadrants overlap. - * \return Returns < 0 if \a v1 < \a v2 and \a v1 and \v2 do not overlap, + * \return Returns < 0 if \a v1 < \a v2 and \a v1 and \a v2 do not overlap, * 0 if \a v1 and \a v2 overlap, - * > 0 if \a v1 > \a v2 and \a v1 and \v2 do not overlap. + * > 0 if \a v1 > \a v2 and \a v1 and \a v2 do not overlap. */ int p8est_quadrant_disjoint (const void *v1, const void *v2); @@ -176,6 +188,13 @@ int p8est_quadrant_ancestor_id (const p8est_quadrant_t * q, */ int p8est_quadrant_child_id (const p8est_quadrant_t * q); +/** Test if Morton indices are inside the unit tree. + * \param [in] coord 3d coordinates. + * \return Returns true if \a (coord[0],coord[1],coord[2]) is inside the unit tree. + */ +int p8est_coordinates_is_inside_root (const p4est_qcoord_t + coord[]); + /** Test if a quadrant is inside the unit tree. * \param [in] q Quadrant to be tested. * \return Returns true if \a q is inside the unit tree. @@ -229,6 +248,14 @@ int p8est_quadrant_is_outside_corner (const p8est_quadrant_t * int p8est_quadrant_is_node (const p8est_quadrant_t * q, int inside); +/** Test if Morton indices are valid and are inside the unit tree. + * \param [in] coord 3d coordinates. + * \param [in] level level + * \return Returns true if \a (coord[0],coord[1],coord[2],level) is valid. + */ +int p8est_coordinates_is_valid (const p4est_qcoord_t coord[], + int level); + /** Test if a quadrant has valid Morton indices and is inside the unit tree. * \param [in] q Quadrant to be tested. * \return Returns true if \a q is valid. @@ -424,18 +451,18 @@ void p8est_quadrant_face_neighbor (const p8est_quadrant_t * q, /** Compute the face neighbor of a quadrant, transforming across tree * boundaries if necessary. * \param [in] q Input quadrant, must be valid. - * \param [in] t Tree that contains \q. + * \param [in] t Tree that contains \a q. * \param [in] face The face across which to generate the neighbor. * \param [in,out] r Existing quadrant whose Morton index will be filled. - * By convention, if there is no tree across \face, - * \r has the same Morton index as \q. - * \param [in,out] nface if not NULL, set to the face of \r that neighbors - * \q. nface is encoded with orientation information + * By convention, if there is no tree across \a face, + * \a r has the same Morton index as \a q. + * \param [in,out] nface if not NULL, set to the face of \a r that neighbors + * \a q. nface is encoded with orientation information * in the same manner as the tree_to_face array in * the p8est_connectivity_t struct. * \param [in] conn The connectivity structure for the forest. - * \return Returns the tree that contains \r. By convention, if there is no - * tree across \face, then -1 is returned. + * \return Returns the tree that contains \a r. By convention, if there is no + * tree across \a face, then -1 is returned. */ p4est_locidx_t p8est_quadrant_face_neighbor_extra (const p8est_quadrant_t * q, p4est_topidx_t t, @@ -499,15 +526,15 @@ void p8est_quadrant_edge_neighbor (const p8est_quadrant_t * q, * boundaries if necessary. Only computes neighbors that are not face * neighbors. * \param [in] q Input quadrant, must be valid. - * \param [in] t Tree that contains \q. + * \param [in] t Tree that contains \a q. * \param [in] edge The edge across which to generate the neighbor. * \param [in,out] quads An initialized but empty array where the edge * neighbors will be placed. * \param [in,out] treeids An initialized but empty array where the ids of the * trees containing the edge neighbors will be placed. * \param [in,out] nedges if not NULL, filled with the edges of \a quads that - * neighbor \q. the ints in \nedges are encoded with - * orientation informatin like the edge_to_edge array + * neighbor \a q. the ints in \a nedges are encoded with + * orientation information like the edge_to_edge array * in the p8est_connectivity_t struct * \param [in] conn The connectivity structure for the forest. */ @@ -534,14 +561,14 @@ void p8est_quadrant_corner_neighbor (const p8est_quadrant_t * * boundaries if necessary. Only computes neighbors that are not face or edge * neighbors. * \param [in] q Input quadrant, must be valid. - * \param [in] t Tree that contains \q. + * \param [in] t Tree that contains \a q. * \param [in] corner The corner across which to generate the neighbor. * \param [in,out] quads An initialized but empty array where the corner * neighbors will be placed. * \param [in,out] treeids An initialized but empty array where the ids of the * trees containing the corner neighbors will be placed. * \param [in,out] ncorners if not NULL, filled with the corners of \a quads - * that neighbor \q. + * that neighbor \a q. * \param [in] conn The connectivity structure for the forest. */ void p8est_quadrant_corner_neighbor_extra (const @@ -671,12 +698,26 @@ void p8est_nearest_common_ancestor_D (const p8est_quadrant_t * * [0]..[2] The coordinate axis sequence of the origin face. * [3]..[5] The coordinate axis sequence of the target face. * [6]..[8] Edge reverse flag for axes 0, 1; face code for 2. - * \note \a q and \q r may NOT point to the same quadrant structure. + * \note \a q and \a r may NOT point to the same quadrant structure. */ void p8est_quadrant_transform_face (const p8est_quadrant_t * q, p8est_quadrant_t * r, const int ftransform[]); +/** Transforms coordinates across a face between trees. + * \param [in] coords_in Input coordinates. + * \param [out] coords_out Output coordinates. + * \param [in] ftransform This array holds 9 integers. + * [0]..[2] The coordinate axis sequence of the origin face. + * [3]..[5] The coordinate axis sequence of the target face. + * [6]..[8] Edge reverse flag for axes 0, 1; face code for 2. + */ +void p8est_coordinates_transform_face (const p4est_qcoord_t + coords_in[], + p4est_qcoord_t + coords_out[], + const int ftransform[]); + /** Checks if a quadrant touches an edge (diagonally inside or outside). */ int p8est_quadrant_touches_edge (const p8est_quadrant_t * q, @@ -685,8 +726,8 @@ int p8est_quadrant_touches_edge (const p8est_quadrant_t * q, /** Transforms a quadrant across an edge between trees. * \param [in] q Input quadrant. * \param [in,out] r Quadrant whose Morton index will be filled. - * \param [in] edge Edge index of the originating quadrant. - * \param [in] ei Edge information computed previously. + * \param [in] ei Edge info from p8est_find_edge_transform(). + * \param [in] et One of ei's transformations. * \param [in] inside The quadrant will be placed inside or outside. */ void p8est_quadrant_transform_edge (const p8est_quadrant_t * q, @@ -697,6 +738,23 @@ void p8est_quadrant_transform_edge (const p8est_quadrant_t * q, p8est_edge_transform_t * et, int inside); +/** Transforms coordinates on an edge between trees. + * \param [in] coords_in Input coordinates. + * \param [out] coords_out Output coordinates. + * \param [in] ei Edge info from p8est_find_edge_transform(). + * \param [in] et One of ei's transformations. + */ +void p8est_coordinates_transform_edge (const p4est_qcoord_t + coords_in[], + p4est_qcoord_t + coords_out[], + const + p8est_edge_info_t * + ei, + const + p8est_edge_transform_t * + et); + /** Shifts a quadrant until it touches the specified edge from the inside. * If this shift is meant to recreate the effects of \a q on balancing across * the edge, then \a r, \a rup, and \a rdown may all be necessary for that @@ -800,6 +858,61 @@ void p8est_quadrant_predecessor (const p8est_quadrant_t * void p8est_quadrant_srand (const p8est_quadrant_t * q, sc_rand_state_t * rstate); +/** Transform a quadrant from self's coordinate system to neighbor's coordinate system. + * + * \param [in] nt A neighbor transform. + * \param [in] self_quad Input quadrant in self coordinates. + * \param [out] neigh_coords Quad transformed into neighbor coordinates. + * + * \note This transform gives meaningful results when \a self_quad is inside + * the tree root or touches the interface between the two trees in the + * transform. + */ +void p8est_neighbor_transform_quadrant + (const p8est_neighbor_transform_t * nt, + const p8est_quadrant_t * self_quad, p8est_quadrant_t * neigh_quad); + +/** Transform a quadrant from a neighbors's coordinate system to self's coordinate system. + * + * \param [in] nt A neighbor transform. + * \param [in] neigh_coords Input quadrant in neighbor coordinates. + * \param [out] self_coords Quad transformed into self coordinates. + * + * \note This transform gives meaningful results when \a neigh_quad is inside + * the tree root or touches the interface between the two trees in the + * transform. + */ +void p8est_neighbor_transform_quadrant_reverse + (const p8est_neighbor_transform_t * nt, + const p8est_quadrant_t * neigh_quad, p8est_quadrant_t * self_quad); + +/** Check if a descendant shares a face with a (strict) ancestor. + * + * \param [in] descendant The descendant in question. + * \param [in] ancestor The ancestor must not be equal to the descendant. + * \param [in] face The face of the descendant. + * + * \return true if descendant face touches ancestor face, false otherwise. +*/ +int p8est_quadrant_is_ancestor_face (const p8est_quadrant_t * + descendant, + const p8est_quadrant_t * + ancestor, int face); + +/** Check if a descendant shares a corner with a (strict) ancestor. + * + * \param [in] descendant The descendant in question. + * \param [in] ancestor The ancestor must not be equal to the descendant. + * \param [in] corner The corner of the descendant. + * + * \return true if descendant face touches ancestor corner, false otherwise. +*/ +int p8est_quadrant_is_ancestor_corner (const p8est_quadrant_t + * descendant, + const p8est_quadrant_t + * ancestor, + int corner); + SC_EXTERN_C_END; #endif /* !P8EST_BITS_H */ diff --git a/src/p8est_communication.h b/src/p8est_communication.h index ca5e67488..6f2385164 100644 --- a/src/p8est_communication.h +++ b/src/p8est_communication.h @@ -29,6 +29,23 @@ SC_EXTERN_C_BEGIN; +/** Given target, find index p such that `gfq[p] <= target < gfq[p + 1]`. + * \param[in] target The value that is searched in \a gfq. \a target + * has to satisfy `gfq[0] <= target < gfq[nmemb]`. + * \param[in] gfq The sorted array (ascending) in that the function will + * search. + * \param [in] nmemb Number of entries in array MINUS ONE. + * \return Index p such that `gfq[p] <= target < gfq[p + 1]`. + * \note This function differs from \ref p8est_find_partiton + * since \ref p8est_find_partition searches for two + * targets using binary search in an optimized way + * but \ref p8est_bsearch_partition only performs a + * single binary search. + */ +int p8est_bsearch_partition (p4est_gloidx_t target, + const p4est_gloidx_t * gfq, + int nmemb); + /** Assign an MPI communicator to p8est; retrieve parallel environment. * * \param [in] mpicomm A valid MPI communicator. @@ -102,7 +119,7 @@ int p8est_comm_parallel_env_reduce_ext (p8est_t ** int add_to_beginning, int **ranks_subcomm); -/** Caculate the number and partition of quadrants. +/** Calculate the number and partition of quadrants. * \param [in,out] p8est Adds all \c p8est->local_num_quadrant counters and * puts cumulative sums in p8est->global_first_quadrant. */ @@ -120,6 +137,21 @@ void p8est_comm_global_partition (p8est_t * p8est, p8est_quadrant_t * first_quad); +/** Calculate the global fist quadrant array for a uniform partition. + * + * \param [in] global_num_quadrants The global number of quadrants. + * \param [in] mpisize The number of MPI ranks. + * \param [in,out] gfq At least allocated mpisize + 1 + * p4est_gloidx_t. This array is + * filled with the global first + * quadrant array assuming a + * uniform partition. + */ +void p8est_comm_global_first_quadrant (p4est_gloidx_t + global_num_quadrants, + int mpisize, + p4est_gloidx_t * gfq); + /** Compute and distribute the cumulative number of quadrants per tree. * \param [in] p8est This p8est needs to have correct values for * global_first_quadrant and global_first_position. @@ -134,7 +166,27 @@ void p8est_comm_count_pertree (p8est_t * p8est, * \param [in] p Valid processor id. * \return True if and only if processor \p is empty. */ -int p8est_comm_is_empty (p8est_t * p8est, int p); +int p8est_comm_is_empty (p8est_t *p8est, int p); + +/** Query whether a processor has no quadrants. + * \param [in] gfq An array encoding the partition offsets in the + * global quadrant array; length \a num_procs + 1. + * \param [in] num_procs Number of processes in the partition. + * \param [in] p Valid 0 <= \a p < \a num_procs. + * \return True if and only if processor \a p is empty. + */ +int p8est_comm_is_empty_gfq (const p4est_gloidx_t *gfq, + int num_procs, int p); + +/** Query whether a processor has no quadrants. + * \param [in] gfp An array encoding the partition shape. + * Non-decreasing; length \a num_procs + 1. + * \param [in] num_procs Number of processes in the partition. + * \param [in] p Valid 0 <= \a p < \a num_procs. + * \return True if and only if processor \a p is empty. + */ +int p8est_comm_is_empty_gfp (const p8est_quadrant_t *gfp, + int num_procs, int p); /** Test whether a quadrant is fully contained in a rank's owned region. * This function may return false when \ref p8est_comm_is_owner returns true. @@ -147,19 +199,38 @@ int p8est_comm_is_contained (p8est_t * p8est, const p8est_quadrant_t * q, int rank); -/** Test ownershop of a quadrant via p8est->global_first_position. +/** Test ownership of a quadrant via p8est->global_first_position. * The quadrant is considered owned if its first descendant is owned. - * This, a positive result occurs even if its last descendant overlaps + * Thus, a positive result occurs even if its last descendant overlaps * a higher process. - * \param [in] rank Rank whose ownership is tested. - * Assumes a forest with no overlaps. - * \return true if rank is the owner of the first descendant. + * \param [in] p8est Valid forest. + * \param [in] which_tree Valid tree number wrt. the forest. + * \param [in] q Valid quadrant wrt. the forest. + * \param [in] rank Rank whose ownership is tested. + * \return True if rank is the owner of the first descendant. */ -int p8est_comm_is_owner (p8est_t * p8est, +int p8est_comm_is_owner (p8est_t *p8est, p4est_locidx_t which_tree, - const p8est_quadrant_t * q, + const p8est_quadrant_t *q, int rank); +/** Test ownership of a quadrant via a global_first_position array. + * This array encodes part of the partition of a valid forest object. + * The quadrant is considered owned if its first descendant is owned. + * Thus, a positive result occurs even if its last descendant overlaps + * a higher process. + * \param [in] gfp Position array of length \a num_procs + 1. + * \param [in] num_procs Number of processes in this context. + * \param [in] num_trees Number of trees in this context. + * \param [in] which_tree Valid tree number wrt. the forest. + * \param [in] q Valid quadrant wrt. the forest. + * \param [in] rank Rank whose ownership is tested. + * \return True if rank is the owner of the first descendant. + */ +int p8est_comm_is_owner_gfp + (const p8est_quadrant_t *gfp, int num_procs, p4est_topidx_t num_trees, + p4est_locidx_t which_tree, const p8est_quadrant_t *q, int rank); + /** Searches the owner of a quadrant via p8est->global_first_position. * Assumes a tree with no overlaps. * \param [in] guess Initial guess for the search. @@ -213,27 +284,16 @@ int p8est_comm_sync_flag (p8est_t * p8est, /** Compute a parallel partition-independent checksum out of local checksums. * This checksum depends on the global refinement topology. * It does not depend on how the mesh is partitioned. - * The result is available on rank 0. + * The result is available on all processors. * \param [in] p8est The MPI information of this p8est will be used. * \param [in] local_crc Locally computed adler32 checksum. * \param [in] local_bytes Number of bytes used for local checksum. - * \return Parallel checksum on rank 0, 0 otherwise. + * \return Parallel checksum on all processors. */ unsigned p8est_comm_checksum (p8est_t * p8est, unsigned local_crc, size_t local_bytes); -/** Compute a parallel partition-dependent checksum out of local checksums. - * This checksum depends on both the global refinement topology and partition. - * \param [in] p8est The MPI information of this p8est will be used. - * \param [in] local_crc Locally computed adler32 checksum. - * \param [in] local_bytes Number of bytes used for local checksum. - * \return Parallel checksum on rank 0, 0 otherwise. - */ -unsigned p8est_comm_checksum_partition (p8est_t * p8est, - unsigned local_crc, - size_t local_bytes); - /** Context data to allow for split begin/end data transfer. */ typedef struct p8est_transfer_context { @@ -276,23 +336,6 @@ void p8est_transfer_fixed (const p4est_gloidx_t * dest_gfq, const void *src_data, size_t data_size); -/** Given target, find index p such that `gfq[p] <= target < gfq[p + 1]`. - * \param[in] target The value that is searched in \a gfq. \a target - * has to satisfy `gfq[0] <= target < gfq[nmemb]`. - * \param[in] gfq The sorted array (ascending) in that the function will - * search. - * \param [in] nmemb Number of entries in array MINUS ONE. - * \return Index p such that `gfq[p] <= target < gfq[p + 1]`. - * \note This function differs from \ref p8est_find_partiton - * since \ref p8est_find_partition searches for two - * targets using binary search in an optimized way - * but \ref p8est_bsearch_partition only performs a - * single binary search. - */ -int p8est_bsearch_partition (p4est_gloidx_t target, - const p4est_gloidx_t * gfq, - int nmemb); - /** Initiate a fixed-size data transfer between partitions. * See \ref p8est_transfer_fixed for a full description. * Must be matched with \ref p8est_transfer_fixed_end for completion. diff --git a/src/p8est_connectivity.c b/src/p8est_connectivity.c index 9ed1119e5..bc1e8f867 100644 --- a/src/p8est_connectivity.c +++ b/src/p8est_connectivity.c @@ -24,6 +24,7 @@ #include #include +#include /* *INDENT-OFF* */ const int p8est_face_corners[6][4] = @@ -320,6 +321,103 @@ p8est_connectivity_new_rotwrap (void) corner_to_tree, corner_to_corner); } +p4est_connectivity_t * +p8est_connectivity_new_drop (void) +{ +/* *INDENT-OFF* */ + const p4est_topidx_t num_vertices = 24; + const p4est_topidx_t num_trees = 5; + const p4est_topidx_t num_ett = 2; + const p4est_topidx_t num_ctt = 1; + const double vertices[24 * 3] = { + 0, 0, 0, + 1, 0, 0, + 0, 1, 0, + 1, 1, 0, + 3, 1, 0, + 1, 2, 0, + 3, 2, 0, + 0, 0, 1, + 1, 0, 1, + 0, 1, 1, + 1, 1, 1, + 2, 1, 1, + 1, 2, 1, + 2, 2, 1, + 1, 0, 2, + 1, 1, 2, + 2, 1, 2, + 2, 2, 2, + 0, 0, 3, + 0, 1, 3, + 3, 1, 3, + 3, 2, 3, + 0, 2, 3, + 1, 2, 2, + }; + const p4est_topidx_t tree_to_vertex[5 * 8] = { + 0, 1, 2, 3, 7, 8, 9, 10, + 3, 4, 5, 6, 10, 11, 12, 13, + 11, 4, 13, 6, 16, 20, 17, 21, + 15, 16, 23, 17, 19, 20, 22, 21, + 7, 8, 9, 10, 18, 14, 19, 15, + }; + const p4est_topidx_t tree_to_tree[5 * 6] = { + 0, 0, 0, 0, 0, 4, + 1, 2, 1, 1, 1, 1, + 2, 2, 2, 2, 1, 3, + 3, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 0, 4, + }; + const int8_t tree_to_face[5 * 6] = { + 0, 1, 2, 3, 4, 4, + 0, 10, 2, 3, 4, 5, + 0, 1, 2, 3, 7, 1, + 0, 5, 2, 3, 4, 5, + 0, 1, 2, 3, 5, 5, + }; + const p4est_topidx_t tree_to_edge[5 * 12] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, + -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + const p4est_topidx_t ett_offset[2 + 1] = { 0, 2, 4 }; + const p4est_topidx_t edge_to_tree[4] = { + 0, 1, 3, 4 + }; + const int8_t edge_to_edge[4] = { + 11, 8, 8, 15 + }; + const p4est_topidx_t tree_to_corner[5 * 8] = { + -1, -1, -1, -1, -1, -1, -1, 0, + -1, -1, -1, -1, 0, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 0, -1, -1, -1, -1, + }; + const p4est_topidx_t ctt_offset[1 + 1] = { + 0, 3 + }; + const p4est_topidx_t corner_to_tree[3] = { + 0, 1, 4 + }; + const int8_t corner_to_corner[3] = { + 7, 4, 3 + }; +/* *INDENT-ON* */ + + return p4est_connectivity_new_copy (num_vertices, num_trees, + num_ett, num_ctt, + vertices, tree_to_vertex, + tree_to_tree, tree_to_face, + tree_to_edge, ett_offset, + edge_to_tree, edge_to_edge, + tree_to_corner, ctt_offset, + corner_to_tree, corner_to_corner); +} + p4est_connectivity_t * p8est_connectivity_new_twocubes (void) { @@ -902,9 +1000,9 @@ p8est_connectivity_new_torus (int nSegments) const p4est_topidx_t nEdgesPS = 8; /* number of edges per segment */ const p4est_topidx_t num_edges = nEdgesPS*nSegments; const p4est_topidx_t num_corners = 0; - const p4est_topidx_t num_ctt = 0; // corner to tree - const p4est_topidx_t num_ett = nEdgesPS*nSegments*4; // edge to tree - + const p4est_topidx_t num_ctt = 0; /* corner to tree */ + const p4est_topidx_t num_ett = nEdgesPS*nSegments*4; /* edge to tree */ + int i, j, iSegment, nbItems, iTree, iEdge; p4est_connectivity_t *conn; conn = p4est_connectivity_new (num_vertices, num_trees, @@ -927,7 +1025,7 @@ p8est_connectivity_new_torus (int nSegments) 1, 2, 1, }; - for (int i=0; ivertices[i] = vertices[i]; } @@ -941,12 +1039,12 @@ p8est_connectivity_new_torus (int nSegments) 0, 1, 2, 3, 6, 7, 8, 9, /* tree 4 - center*/ }; - int nbItems = nTreesPS*8; // per segments + nbItems = nTreesPS*8; /* per segment */ - // all segments use the same pattern - for (int iSegment=0; iSegmenttree_to_vertex[j+iSegment*nbItems] = tree_to_vertex[j]; } @@ -956,7 +1054,7 @@ p8est_connectivity_new_torus (int nSegments) /* tree to tree */ { - /* retrun global tree id from local tree id and segment id */ + /* return global tree id from local tree id and segment id */ #define tGlob(tLoc,iSeg) ( (tLoc) + (iSeg) * nTreesPS ) /* return global tree id above (+z) */ @@ -973,56 +1071,79 @@ p8est_connectivity_new_torus (int nSegments) /* 2, 0, 1, 3, 4, 4, /\* tree 4 - center *\/ */ /* }; */ - int nbItems = nTreesPS*6; // 5 trees per segment x 6 faces + nbItems = nTreesPS * 6; /* 5 trees per segment x 6 faces */ - /* Globla tree id */ - for (int iSegment=0; iSegmenttree_to_tree[0+iTree*6+iSegment*nbItems] = tGlob(3,iSegment); /* tree = 0 modulo 5 */ - conn->tree_to_tree[1+iTree*6+iSegment*nbItems] = tGlob(1,iSegment); - conn->tree_to_tree[2+iTree*6+iSegment*nbItems] = tGlob(4,iSegment); - conn->tree_to_tree[3+iTree*6+iSegment*nbItems] = tGlob(iTree,iSegment); - conn->tree_to_tree[4+iTree*6+iSegment*nbItems] = tGlobZm(iTree-nTreesPS,iSegment); - conn->tree_to_tree[5+iTree*6+iSegment*nbItems] = tGlobZp(iTree+nTreesPS,iSegment); + conn->tree_to_tree[0 + iTree * 6 + iSegment * nbItems] = tGlob (3, iSegment); /* tree = 0 modulo 5 */ + conn->tree_to_tree[1 + iTree * 6 + iSegment * nbItems] = + tGlob (1, iSegment); + conn->tree_to_tree[2 + iTree * 6 + iSegment * nbItems] = + tGlob (4, iSegment); + conn->tree_to_tree[3 + iTree * 6 + iSegment * nbItems] = + tGlob (iTree, iSegment); + conn->tree_to_tree[4 + iTree * 6 + iSegment * nbItems] = + tGlobZm (iTree - nTreesPS, iSegment); + conn->tree_to_tree[5 + iTree * 6 + iSegment * nbItems] = + tGlobZp (iTree + nTreesPS, iSegment); iTree++; - conn->tree_to_tree[0+iTree*6+iSegment*nbItems] = tGlob(0,iSegment); /* tree = 1 modulo 5 */ - conn->tree_to_tree[1+iTree*6+iSegment*nbItems] = tGlob(2,iSegment); - conn->tree_to_tree[2+iTree*6+iSegment*nbItems] = tGlob(4,iSegment); - conn->tree_to_tree[3+iTree*6+iSegment*nbItems] = tGlob(iTree,iSegment); - conn->tree_to_tree[4+iTree*6+iSegment*nbItems] = tGlobZm(iTree-nTreesPS,iSegment); - conn->tree_to_tree[5+iTree*6+iSegment*nbItems] = tGlobZp(iTree+nTreesPS,iSegment); + conn->tree_to_tree[0 + iTree * 6 + iSegment * nbItems] = tGlob (0, iSegment); /* tree = 1 modulo 5 */ + conn->tree_to_tree[1 + iTree * 6 + iSegment * nbItems] = + tGlob (2, iSegment); + conn->tree_to_tree[2 + iTree * 6 + iSegment * nbItems] = + tGlob (4, iSegment); + conn->tree_to_tree[3 + iTree * 6 + iSegment * nbItems] = + tGlob (iTree, iSegment); + conn->tree_to_tree[4 + iTree * 6 + iSegment * nbItems] = + tGlobZm (iTree - nTreesPS, iSegment); + conn->tree_to_tree[5 + iTree * 6 + iSegment * nbItems] = + tGlobZp (iTree + nTreesPS, iSegment); iTree++; - conn->tree_to_tree[0+iTree*6+iSegment*nbItems] = tGlob(1,iSegment); /* tree = 2 modulo 5 */ - conn->tree_to_tree[1+iTree*6+iSegment*nbItems] = tGlob(3,iSegment); - conn->tree_to_tree[2+iTree*6+iSegment*nbItems] = tGlob(4,iSegment); - conn->tree_to_tree[3+iTree*6+iSegment*nbItems] = tGlob(iTree,iSegment); - conn->tree_to_tree[4+iTree*6+iSegment*nbItems] = tGlobZm(iTree-nTreesPS,iSegment); - conn->tree_to_tree[5+iTree*6+iSegment*nbItems] = tGlobZp(iTree+nTreesPS,iSegment); + conn->tree_to_tree[0 + iTree * 6 + iSegment * nbItems] = tGlob (1, iSegment); /* tree = 2 modulo 5 */ + conn->tree_to_tree[1 + iTree * 6 + iSegment * nbItems] = + tGlob (3, iSegment); + conn->tree_to_tree[2 + iTree * 6 + iSegment * nbItems] = + tGlob (4, iSegment); + conn->tree_to_tree[3 + iTree * 6 + iSegment * nbItems] = + tGlob (iTree, iSegment); + conn->tree_to_tree[4 + iTree * 6 + iSegment * nbItems] = + tGlobZm (iTree - nTreesPS, iSegment); + conn->tree_to_tree[5 + iTree * 6 + iSegment * nbItems] = + tGlobZp (iTree + nTreesPS, iSegment); iTree++; - conn->tree_to_tree[0+iTree*6+iSegment*nbItems] = tGlob(2,iSegment); /* tree = 3 modulo 5 */ - conn->tree_to_tree[1+iTree*6+iSegment*nbItems] = tGlob(0,iSegment); - conn->tree_to_tree[2+iTree*6+iSegment*nbItems] = tGlob(4,iSegment); - conn->tree_to_tree[3+iTree*6+iSegment*nbItems] = tGlob(iTree,iSegment); // self - conn->tree_to_tree[4+iTree*6+iSegment*nbItems] = tGlobZm(iTree-nTreesPS,iSegment); - conn->tree_to_tree[5+iTree*6+iSegment*nbItems] = tGlobZp(iTree+nTreesPS,iSegment); + conn->tree_to_tree[0 + iTree * 6 + iSegment * nbItems] = tGlob (2, iSegment); /* tree = 3 modulo 5 */ + conn->tree_to_tree[1 + iTree * 6 + iSegment * nbItems] = + tGlob (0, iSegment); + conn->tree_to_tree[2 + iTree * 6 + iSegment * nbItems] = + tGlob (4, iSegment); + conn->tree_to_tree[3 + iTree * 6 + iSegment * nbItems] = tGlob (iTree, iSegment); /* self */ + conn->tree_to_tree[4 + iTree * 6 + iSegment * nbItems] = + tGlobZm (iTree - nTreesPS, iSegment); + conn->tree_to_tree[5 + iTree * 6 + iSegment * nbItems] = + tGlobZp (iTree + nTreesPS, iSegment); iTree++; - conn->tree_to_tree[0+iTree*6+iSegment*nbItems] = tGlob(2,iSegment); /* tree = 4 modulo 5 */ - conn->tree_to_tree[1+iTree*6+iSegment*nbItems] = tGlob(0,iSegment); - conn->tree_to_tree[2+iTree*6+iSegment*nbItems] = tGlob(1,iSegment); - conn->tree_to_tree[3+iTree*6+iSegment*nbItems] = tGlob(3,iSegment); - conn->tree_to_tree[4+iTree*6+iSegment*nbItems] = tGlobZm(iTree-nTreesPS,iSegment); - conn->tree_to_tree[5+iTree*6+iSegment*nbItems] = tGlobZp(iTree+nTreesPS,iSegment); + conn->tree_to_tree[0 + iTree * 6 + iSegment * nbItems] = tGlob (2, iSegment); /* tree = 4 modulo 5 */ + conn->tree_to_tree[1 + iTree * 6 + iSegment * nbItems] = + tGlob (0, iSegment); + conn->tree_to_tree[2 + iTree * 6 + iSegment * nbItems] = + tGlob (1, iSegment); + conn->tree_to_tree[3 + iTree * 6 + iSegment * nbItems] = + tGlob (3, iSegment); + conn->tree_to_tree[4 + iTree * 6 + iSegment * nbItems] = + tGlobZm (iTree - nTreesPS, iSegment); + conn->tree_to_tree[5 + iTree * 6 + iSegment * nbItems] = + tGlobZp (iTree + nTreesPS, iSegment); iTree++; @@ -1044,49 +1165,48 @@ p8est_connectivity_new_torus (int nSegments) /* 2, 8, 8, 2, 4, 5, /\* tree 4 - center *\/ */ /* }; */ - // all segments use the same pattern - int iTree = 0; // global treeId - for (int iSegment=0; iSegmenttree_to_face[0+iTree*6] = 1; /* tree = 0 modulo 5 */ - conn->tree_to_face[1+iTree*6] = 0; - conn->tree_to_face[2+iTree*6] = 7; - conn->tree_to_face[3+iTree*6] = 3; - conn->tree_to_face[4+iTree*6] = 5; - conn->tree_to_face[5+iTree*6] = 4; + conn->tree_to_face[0 + iTree * 6] = 1; /* tree = 0 modulo 5 */ + conn->tree_to_face[1 + iTree * 6] = 0; + conn->tree_to_face[2 + iTree * 6] = 7; + conn->tree_to_face[3 + iTree * 6] = 3; + conn->tree_to_face[4 + iTree * 6] = 5; + conn->tree_to_face[5 + iTree * 6] = 4; iTree++; - conn->tree_to_face[0+iTree*6] = 1; /* tree = 1 modulo 5 */ - conn->tree_to_face[1+iTree*6] = 0; - conn->tree_to_face[2+iTree*6] = 8; - conn->tree_to_face[3+iTree*6] = 3; - conn->tree_to_face[4+iTree*6] = 5; - conn->tree_to_face[5+iTree*6] = 4; + conn->tree_to_face[0 + iTree * 6] = 1; /* tree = 1 modulo 5 */ + conn->tree_to_face[1 + iTree * 6] = 0; + conn->tree_to_face[2 + iTree * 6] = 8; + conn->tree_to_face[3 + iTree * 6] = 3; + conn->tree_to_face[4 + iTree * 6] = 5; + conn->tree_to_face[5 + iTree * 6] = 4; iTree++; - conn->tree_to_face[0+iTree*6] = 1; /* tree = 2 modulo 5 */ - conn->tree_to_face[1+iTree*6] = 0; - conn->tree_to_face[2+iTree*6] = 0; - conn->tree_to_face[3+iTree*6] = 3; - conn->tree_to_face[4+iTree*6] = 5; - conn->tree_to_face[5+iTree*6] = 4; + conn->tree_to_face[0 + iTree * 6] = 1; /* tree = 2 modulo 5 */ + conn->tree_to_face[1 + iTree * 6] = 0; + conn->tree_to_face[2 + iTree * 6] = 0; + conn->tree_to_face[3 + iTree * 6] = 3; + conn->tree_to_face[4 + iTree * 6] = 5; + conn->tree_to_face[5 + iTree * 6] = 4; iTree++; - conn->tree_to_face[0+iTree*6] = 1; /* tree = 3 modulo 5 */ - conn->tree_to_face[1+iTree*6] = 0; - conn->tree_to_face[2+iTree*6] = 3; - conn->tree_to_face[3+iTree*6] = 3; - conn->tree_to_face[4+iTree*6] = 5; - conn->tree_to_face[5+iTree*6] = 4; + conn->tree_to_face[0 + iTree * 6] = 1; /* tree = 3 modulo 5 */ + conn->tree_to_face[1 + iTree * 6] = 0; + conn->tree_to_face[2 + iTree * 6] = 3; + conn->tree_to_face[3 + iTree * 6] = 3; + conn->tree_to_face[4 + iTree * 6] = 5; + conn->tree_to_face[5 + iTree * 6] = 4; iTree++; - conn->tree_to_face[0+iTree*6] = 2; /* tree = 4 modulo 5 */ - conn->tree_to_face[1+iTree*6] = 8; - conn->tree_to_face[2+iTree*6] = 8; - conn->tree_to_face[3+iTree*6] = 2; - conn->tree_to_face[4+iTree*6] = 5; - conn->tree_to_face[5+iTree*6] = 4; + conn->tree_to_face[0 + iTree * 6] = 2; /* tree = 4 modulo 5 */ + conn->tree_to_face[1 + iTree * 6] = 8; + conn->tree_to_face[2 + iTree * 6] = 8; + conn->tree_to_face[3 + iTree * 6] = 2; + conn->tree_to_face[4 + iTree * 6] = 5; + conn->tree_to_face[5 + iTree * 6] = 4; iTree++; } @@ -1101,83 +1221,82 @@ p8est_connectivity_new_torus (int nSegments) /* same as above but taking into account torus periodicity */ #define eGlob2(eLoc,iSeg) ( eGlob((eLoc),(iSeg)) < num_edges ? eGlob((eLoc),(iSeg)) : eGlob((eLoc),(iSeg)) - num_edges ) - // all segments use the same pattern - int iTree = 0; /* global tree id */ - for (int iSegment=0; iSegmenttree_to_edge[ 0+iTree*12] = eGlob(0,iSegment); - conn->tree_to_edge[ 1+iTree*12] = -1; - conn->tree_to_edge[ 2+iTree*12] = eGlob2(0+nEdgesPS,iSegment); - conn->tree_to_edge[ 3+iTree*12] = -1; - conn->tree_to_edge[ 4+iTree*12] = eGlob(7,iSegment); - conn->tree_to_edge[ 5+iTree*12] = eGlob(4,iSegment); - conn->tree_to_edge[ 6+iTree*12] = eGlob2(7+nEdgesPS,iSegment); - conn->tree_to_edge[ 7+iTree*12] = eGlob2(4+nEdgesPS,iSegment); - conn->tree_to_edge[ 8+iTree*12] = -1; - conn->tree_to_edge[ 9+iTree*12] = -1; - conn->tree_to_edge[10+iTree*12] = -1; - conn->tree_to_edge[11+iTree*12] = -1; + conn->tree_to_edge[0 + iTree * 12] = eGlob (0, iSegment); + conn->tree_to_edge[1 + iTree * 12] = -1; + conn->tree_to_edge[2 + iTree * 12] = eGlob2 (0 + nEdgesPS, iSegment); + conn->tree_to_edge[3 + iTree * 12] = -1; + conn->tree_to_edge[4 + iTree * 12] = eGlob (7, iSegment); + conn->tree_to_edge[5 + iTree * 12] = eGlob (4, iSegment); + conn->tree_to_edge[6 + iTree * 12] = eGlob2 (7 + nEdgesPS, iSegment); + conn->tree_to_edge[7 + iTree * 12] = eGlob2 (4 + nEdgesPS, iSegment); + conn->tree_to_edge[8 + iTree * 12] = -1; + conn->tree_to_edge[9 + iTree * 12] = -1; + conn->tree_to_edge[10 + iTree * 12] = -1; + conn->tree_to_edge[11 + iTree * 12] = -1; iTree++; /* tree = 1 modulo 5 */ - conn->tree_to_edge[ 0+iTree*12] = eGlob(1,iSegment); - conn->tree_to_edge[ 1+iTree*12] = -1; - conn->tree_to_edge[ 2+iTree*12] = eGlob2(1+nEdgesPS,iSegment); - conn->tree_to_edge[ 3+iTree*12] = -1; - conn->tree_to_edge[ 4+iTree*12] = eGlob(4,iSegment); - conn->tree_to_edge[ 5+iTree*12] = eGlob(5,iSegment); - conn->tree_to_edge[ 6+iTree*12] = eGlob2(4+nEdgesPS,iSegment); - conn->tree_to_edge[ 7+iTree*12] = eGlob2(5+nEdgesPS,iSegment); - conn->tree_to_edge[ 8+iTree*12] = -1; - conn->tree_to_edge[ 9+iTree*12] = -1; - conn->tree_to_edge[10+iTree*12] = -1; - conn->tree_to_edge[11+iTree*12] = -1; + conn->tree_to_edge[0 + iTree * 12] = eGlob (1, iSegment); + conn->tree_to_edge[1 + iTree * 12] = -1; + conn->tree_to_edge[2 + iTree * 12] = eGlob2 (1 + nEdgesPS, iSegment); + conn->tree_to_edge[3 + iTree * 12] = -1; + conn->tree_to_edge[4 + iTree * 12] = eGlob (4, iSegment); + conn->tree_to_edge[5 + iTree * 12] = eGlob (5, iSegment); + conn->tree_to_edge[6 + iTree * 12] = eGlob2 (4 + nEdgesPS, iSegment); + conn->tree_to_edge[7 + iTree * 12] = eGlob2 (5 + nEdgesPS, iSegment); + conn->tree_to_edge[8 + iTree * 12] = -1; + conn->tree_to_edge[9 + iTree * 12] = -1; + conn->tree_to_edge[10 + iTree * 12] = -1; + conn->tree_to_edge[11 + iTree * 12] = -1; iTree++; /* tree = 2 modulo 5 */ - conn->tree_to_edge[ 0+iTree*12] = eGlob(2,iSegment); - conn->tree_to_edge[ 1+iTree*12] = -1; - conn->tree_to_edge[ 2+iTree*12] = eGlob2(2+nEdgesPS,iSegment); - conn->tree_to_edge[ 3+iTree*12] = -1; - conn->tree_to_edge[ 4+iTree*12] = eGlob(5,iSegment); - conn->tree_to_edge[ 5+iTree*12] = eGlob(6,iSegment); - conn->tree_to_edge[ 6+iTree*12] = eGlob2(5+nEdgesPS,iSegment); - conn->tree_to_edge[ 7+iTree*12] = eGlob2(6+nEdgesPS,iSegment); - conn->tree_to_edge[ 8+iTree*12] = -1; - conn->tree_to_edge[ 9+iTree*12] = -1; - conn->tree_to_edge[10+iTree*12] = -1; - conn->tree_to_edge[11+iTree*12] = -1; + conn->tree_to_edge[0 + iTree * 12] = eGlob (2, iSegment); + conn->tree_to_edge[1 + iTree * 12] = -1; + conn->tree_to_edge[2 + iTree * 12] = eGlob2 (2 + nEdgesPS, iSegment); + conn->tree_to_edge[3 + iTree * 12] = -1; + conn->tree_to_edge[4 + iTree * 12] = eGlob (5, iSegment); + conn->tree_to_edge[5 + iTree * 12] = eGlob (6, iSegment); + conn->tree_to_edge[6 + iTree * 12] = eGlob2 (5 + nEdgesPS, iSegment); + conn->tree_to_edge[7 + iTree * 12] = eGlob2 (6 + nEdgesPS, iSegment); + conn->tree_to_edge[8 + iTree * 12] = -1; + conn->tree_to_edge[9 + iTree * 12] = -1; + conn->tree_to_edge[10 + iTree * 12] = -1; + conn->tree_to_edge[11 + iTree * 12] = -1; iTree++; /* tree = 3 modulo 5 */ - conn->tree_to_edge[ 0+iTree*12] = eGlob(3,iSegment); - conn->tree_to_edge[ 1+iTree*12] = -1; - conn->tree_to_edge[ 2+iTree*12] = eGlob2(3+nEdgesPS,iSegment); - conn->tree_to_edge[ 3+iTree*12] = -1; - conn->tree_to_edge[ 4+iTree*12] = eGlob(6,iSegment); - conn->tree_to_edge[ 5+iTree*12] = eGlob(7,iSegment); - conn->tree_to_edge[ 6+iTree*12] = eGlob2(6+nEdgesPS,iSegment); - conn->tree_to_edge[ 7+iTree*12] = eGlob2(7+nEdgesPS,iSegment); - conn->tree_to_edge[ 8+iTree*12] = -1; - conn->tree_to_edge[ 9+iTree*12] = -1; - conn->tree_to_edge[10+iTree*12] = -1; - conn->tree_to_edge[11+iTree*12] = -1; + conn->tree_to_edge[0 + iTree * 12] = eGlob (3, iSegment); + conn->tree_to_edge[1 + iTree * 12] = -1; + conn->tree_to_edge[2 + iTree * 12] = eGlob2 (3 + nEdgesPS, iSegment); + conn->tree_to_edge[3 + iTree * 12] = -1; + conn->tree_to_edge[4 + iTree * 12] = eGlob (6, iSegment); + conn->tree_to_edge[5 + iTree * 12] = eGlob (7, iSegment); + conn->tree_to_edge[6 + iTree * 12] = eGlob2 (6 + nEdgesPS, iSegment); + conn->tree_to_edge[7 + iTree * 12] = eGlob2 (7 + nEdgesPS, iSegment); + conn->tree_to_edge[8 + iTree * 12] = -1; + conn->tree_to_edge[9 + iTree * 12] = -1; + conn->tree_to_edge[10 + iTree * 12] = -1; + conn->tree_to_edge[11 + iTree * 12] = -1; iTree++; /* tree = 4 modulo 5 */ - conn->tree_to_edge[ 0+iTree*12] = eGlob(1,iSegment); - conn->tree_to_edge[ 1+iTree*12] = eGlob(3,iSegment); - conn->tree_to_edge[ 2+iTree*12] = eGlob2(1+nEdgesPS,iSegment); - conn->tree_to_edge[ 3+iTree*12] = eGlob2(3+nEdgesPS,iSegment); - conn->tree_to_edge[ 4+iTree*12] = eGlob(2,iSegment); - conn->tree_to_edge[ 5+iTree*12] = eGlob(0,iSegment); - conn->tree_to_edge[ 6+iTree*12] = eGlob2(2+nEdgesPS,iSegment); - conn->tree_to_edge[ 7+iTree*12] = eGlob2(0+nEdgesPS,iSegment); - conn->tree_to_edge[ 8+iTree*12] = -1; - conn->tree_to_edge[ 9+iTree*12] = -1; - conn->tree_to_edge[10+iTree*12] = -1; - conn->tree_to_edge[11+iTree*12] = -1; + conn->tree_to_edge[0 + iTree * 12] = eGlob (1, iSegment); + conn->tree_to_edge[1 + iTree * 12] = eGlob (3, iSegment); + conn->tree_to_edge[2 + iTree * 12] = eGlob2 (1 + nEdgesPS, iSegment); + conn->tree_to_edge[3 + iTree * 12] = eGlob2 (3 + nEdgesPS, iSegment); + conn->tree_to_edge[4 + iTree * 12] = eGlob (2, iSegment); + conn->tree_to_edge[5 + iTree * 12] = eGlob (0, iSegment); + conn->tree_to_edge[6 + iTree * 12] = eGlob2 (2 + nEdgesPS, iSegment); + conn->tree_to_edge[7 + iTree * 12] = eGlob2 (0 + nEdgesPS, iSegment); + conn->tree_to_edge[8 + iTree * 12] = -1; + conn->tree_to_edge[9 + iTree * 12] = -1; + conn->tree_to_edge[10 + iTree * 12] = -1; + conn->tree_to_edge[11 + iTree * 12] = -1; iTree++; } @@ -1190,117 +1309,114 @@ p8est_connectivity_new_torus (int nSegments) #define tGlob2(tLoc,iSeg) ( ( tGlob(tLoc,iSeg) < 0) ? tGlob(tLoc,iSeg) + num_trees : tGlob(tLoc,iSeg) ) /* remember, there are 8 edges per segments */ - int iEdge = 0; - for (int iSegment=0; iSegmentedge_to_tree[iEdge+0] = tGlob2(0,iSegment); - conn->edge_to_tree[iEdge+1] = tGlob2(4,iSegment); - conn->edge_to_tree[iEdge+2] = tGlob2(0-nTreesPS,iSegment); - conn->edge_to_tree[iEdge+3] = tGlob2(4-nTreesPS,iSegment); - iEdge+=4; - - conn->edge_to_tree[iEdge+0] = tGlob2(1,iSegment); - conn->edge_to_tree[iEdge+1] = tGlob2(4,iSegment); - conn->edge_to_tree[iEdge+2] = tGlob2(1-nTreesPS,iSegment); - conn->edge_to_tree[iEdge+3] = tGlob2(4-nTreesPS,iSegment); - iEdge+=4; - - conn->edge_to_tree[iEdge+0] = tGlob2(2,iSegment); - conn->edge_to_tree[iEdge+1] = tGlob2(4,iSegment); - conn->edge_to_tree[iEdge+2] = tGlob2(2-nTreesPS,iSegment); - conn->edge_to_tree[iEdge+3] = tGlob2(4-nTreesPS,iSegment); - iEdge+=4; - - conn->edge_to_tree[iEdge+0] = tGlob2(3,iSegment); - conn->edge_to_tree[iEdge+1] = tGlob2(4,iSegment); - conn->edge_to_tree[iEdge+2] = tGlob2(3-nTreesPS,iSegment); - conn->edge_to_tree[iEdge+3] = tGlob2(4-nTreesPS,iSegment); - iEdge+=4; - - conn->edge_to_tree[iEdge+0] = tGlob2(0,iSegment); - conn->edge_to_tree[iEdge+1] = tGlob2(1,iSegment); - conn->edge_to_tree[iEdge+2] = tGlob2(0-nTreesPS,iSegment); - conn->edge_to_tree[iEdge+3] = tGlob2(1-nTreesPS,iSegment); - iEdge+=4; - - conn->edge_to_tree[iEdge+0] = tGlob2(1,iSegment); - conn->edge_to_tree[iEdge+1] = tGlob2(2,iSegment); - conn->edge_to_tree[iEdge+2] = tGlob2(1-nTreesPS,iSegment); - conn->edge_to_tree[iEdge+3] = tGlob2(2-nTreesPS,iSegment); - iEdge+=4; - - conn->edge_to_tree[iEdge+0] = tGlob2(2,iSegment); - conn->edge_to_tree[iEdge+1] = tGlob2(3,iSegment); - conn->edge_to_tree[iEdge+2] = tGlob2(2-nTreesPS,iSegment); - conn->edge_to_tree[iEdge+3] = tGlob2(3-nTreesPS,iSegment); - iEdge+=4; - - conn->edge_to_tree[iEdge+0] = tGlob2(3,iSegment); - conn->edge_to_tree[iEdge+1] = tGlob2(0,iSegment); - conn->edge_to_tree[iEdge+2] = tGlob2(3-nTreesPS,iSegment); - conn->edge_to_tree[iEdge+3] = tGlob2(0-nTreesPS,iSegment); - iEdge+=4; + iEdge = 0; + for (iSegment = 0; iSegment < nSegments; ++iSegment) { + conn->edge_to_tree[iEdge + 0] = tGlob2 (0, iSegment); + conn->edge_to_tree[iEdge + 1] = tGlob2 (4, iSegment); + conn->edge_to_tree[iEdge + 2] = tGlob2 (0 - nTreesPS, iSegment); + conn->edge_to_tree[iEdge + 3] = tGlob2 (4 - nTreesPS, iSegment); + iEdge += 4; + + conn->edge_to_tree[iEdge + 0] = tGlob2 (1, iSegment); + conn->edge_to_tree[iEdge + 1] = tGlob2 (4, iSegment); + conn->edge_to_tree[iEdge + 2] = tGlob2 (1 - nTreesPS, iSegment); + conn->edge_to_tree[iEdge + 3] = tGlob2 (4 - nTreesPS, iSegment); + iEdge += 4; + + conn->edge_to_tree[iEdge + 0] = tGlob2 (2, iSegment); + conn->edge_to_tree[iEdge + 1] = tGlob2 (4, iSegment); + conn->edge_to_tree[iEdge + 2] = tGlob2 (2 - nTreesPS, iSegment); + conn->edge_to_tree[iEdge + 3] = tGlob2 (4 - nTreesPS, iSegment); + iEdge += 4; + + conn->edge_to_tree[iEdge + 0] = tGlob2 (3, iSegment); + conn->edge_to_tree[iEdge + 1] = tGlob2 (4, iSegment); + conn->edge_to_tree[iEdge + 2] = tGlob2 (3 - nTreesPS, iSegment); + conn->edge_to_tree[iEdge + 3] = tGlob2 (4 - nTreesPS, iSegment); + iEdge += 4; + + conn->edge_to_tree[iEdge + 0] = tGlob2 (0, iSegment); + conn->edge_to_tree[iEdge + 1] = tGlob2 (1, iSegment); + conn->edge_to_tree[iEdge + 2] = tGlob2 (0 - nTreesPS, iSegment); + conn->edge_to_tree[iEdge + 3] = tGlob2 (1 - nTreesPS, iSegment); + iEdge += 4; + + conn->edge_to_tree[iEdge + 0] = tGlob2 (1, iSegment); + conn->edge_to_tree[iEdge + 1] = tGlob2 (2, iSegment); + conn->edge_to_tree[iEdge + 2] = tGlob2 (1 - nTreesPS, iSegment); + conn->edge_to_tree[iEdge + 3] = tGlob2 (2 - nTreesPS, iSegment); + iEdge += 4; + + conn->edge_to_tree[iEdge + 0] = tGlob2 (2, iSegment); + conn->edge_to_tree[iEdge + 1] = tGlob2 (3, iSegment); + conn->edge_to_tree[iEdge + 2] = tGlob2 (2 - nTreesPS, iSegment); + conn->edge_to_tree[iEdge + 3] = tGlob2 (3 - nTreesPS, iSegment); + iEdge += 4; + + conn->edge_to_tree[iEdge + 0] = tGlob2 (3, iSegment); + conn->edge_to_tree[iEdge + 1] = tGlob2 (0, iSegment); + conn->edge_to_tree[iEdge + 2] = tGlob2 (3 - nTreesPS, iSegment); + conn->edge_to_tree[iEdge + 3] = tGlob2 (0 - nTreesPS, iSegment); + iEdge += 4; } #undef tGlob2 #undef tGlob - for (int i = 0; i<=nEdgesPS*nSegments; ++i) - { - conn->ett_offset[i] = 4*i; + for (i = 0; i <= nEdgesPS * nSegments; ++i) { + conn->ett_offset[i] = 4 * i; } /* edge to edge */ iEdge = 0; - for (int iSegment=0; iSegmentedge_to_edge[iEdge+0] = 0; - conn->edge_to_edge[iEdge+1] = 17; - conn->edge_to_edge[iEdge+2] = 2; - conn->edge_to_edge[iEdge+3] = 19; - iEdge+=4; - - conn->edge_to_edge[iEdge+0] = 0; - conn->edge_to_edge[iEdge+1] = 12; - conn->edge_to_edge[iEdge+2] = 2; - conn->edge_to_edge[iEdge+3] = 14; - iEdge+=4; - - conn->edge_to_edge[iEdge+0] = 0; - conn->edge_to_edge[iEdge+1] = 4; - conn->edge_to_edge[iEdge+2] = 2; - conn->edge_to_edge[iEdge+3] = 6; - iEdge+=4; - - conn->edge_to_edge[iEdge+0] = 0; - conn->edge_to_edge[iEdge+1] = 1; - conn->edge_to_edge[iEdge+2] = 2; - conn->edge_to_edge[iEdge+3] = 3; - iEdge+=4; - - conn->edge_to_edge[iEdge+0] = 5; - conn->edge_to_edge[iEdge+1] = 4; - conn->edge_to_edge[iEdge+2] = 7; - conn->edge_to_edge[iEdge+3] = 6; - iEdge+=4; - - conn->edge_to_edge[iEdge+0] = 5; - conn->edge_to_edge[iEdge+1] = 4; - conn->edge_to_edge[iEdge+2] = 7; - conn->edge_to_edge[iEdge+3] = 6; - iEdge+=4; - - conn->edge_to_edge[iEdge+0] = 5; - conn->edge_to_edge[iEdge+1] = 4; - conn->edge_to_edge[iEdge+2] = 7; - conn->edge_to_edge[iEdge+3] = 6; - iEdge+=4; - - conn->edge_to_edge[iEdge+0] = 5; - conn->edge_to_edge[iEdge+1] = 4; - conn->edge_to_edge[iEdge+2] = 7; - conn->edge_to_edge[iEdge+3] = 6; - iEdge+=4; + for (iSegment = 0; iSegment < nSegments; ++iSegment) { + conn->edge_to_edge[iEdge + 0] = 0; + conn->edge_to_edge[iEdge + 1] = 17; + conn->edge_to_edge[iEdge + 2] = 2; + conn->edge_to_edge[iEdge + 3] = 19; + iEdge += 4; + + conn->edge_to_edge[iEdge + 0] = 0; + conn->edge_to_edge[iEdge + 1] = 12; + conn->edge_to_edge[iEdge + 2] = 2; + conn->edge_to_edge[iEdge + 3] = 14; + iEdge += 4; + + conn->edge_to_edge[iEdge + 0] = 0; + conn->edge_to_edge[iEdge + 1] = 4; + conn->edge_to_edge[iEdge + 2] = 2; + conn->edge_to_edge[iEdge + 3] = 6; + iEdge += 4; + + conn->edge_to_edge[iEdge + 0] = 0; + conn->edge_to_edge[iEdge + 1] = 1; + conn->edge_to_edge[iEdge + 2] = 2; + conn->edge_to_edge[iEdge + 3] = 3; + iEdge += 4; + + conn->edge_to_edge[iEdge + 0] = 5; + conn->edge_to_edge[iEdge + 1] = 4; + conn->edge_to_edge[iEdge + 2] = 7; + conn->edge_to_edge[iEdge + 3] = 6; + iEdge += 4; + + conn->edge_to_edge[iEdge + 0] = 5; + conn->edge_to_edge[iEdge + 1] = 4; + conn->edge_to_edge[iEdge + 2] = 7; + conn->edge_to_edge[iEdge + 3] = 6; + iEdge += 4; + + conn->edge_to_edge[iEdge + 0] = 5; + conn->edge_to_edge[iEdge + 1] = 4; + conn->edge_to_edge[iEdge + 2] = 7; + conn->edge_to_edge[iEdge + 3] = 6; + iEdge += 4; + + conn->edge_to_edge[iEdge + 0] = 5; + conn->edge_to_edge[iEdge + 1] = 4; + conn->edge_to_edge[iEdge + 2] = 7; + conn->edge_to_edge[iEdge + 3] = 6; + iEdge += 4; } } diff --git a/src/p8est_connectivity.h b/src/p8est_connectivity.h index 47089522f..26828b5bb 100644 --- a/src/p8est_connectivity.h +++ b/src/p8est_connectivity.h @@ -24,7 +24,36 @@ /** \file p8est_connectivity.h * - * The coarse topological description of the forest. + * The connectivity defines the coarse topology of the forest. + * + * A 3D forest consists of one or more octrees, each of which a logical + * cube. + * Each tree has a local coordinate system, which defines the origin and the + * direction of its x-, y-, and z-axes as well as the numbering of its + * faces, edges, and corners. + * Each tree may connect to any other tree (including itself) across any of + * its faces, edges and/or corners, where the neighbor may be arbitrarily + * rotated and/or flipped. + * The \ref p8est_connectivity data structure stores these connections. + * + * We impose the following requirement for consistency of \ref p8est_balance : + * + * \note If a connectivity implies natural connections between trees that + * are edge neighbors without being face neighbors, these edges shall be + * encoded explicitly in the connectivity. If a connectivity implies + * natural connections between trees that are corner neighbors without being + * edge or face neighbors, these corners shall be encoded explicitly in the + * connectivity. + * Please see the documentation of \ref p8est_connectivity_t for the exact + * encoding convention. + * + * We provide various predefined connectivities by dedicated constructors, + * such as + * + * * \ref p8est_connectivity_new_unitcube for the unit square, + * * \ref p8est_connectivity_new_periodic for the periodic unit square, + * * \ref p8est_connectivity_new_brick for a rectangular grid of trees, + * * \ref p8est_connectivity_new_drop for a sparsely loop of trees. * * \ingroup p8est */ @@ -64,13 +93,13 @@ SC_EXTERN_C_BEGIN; /** Exponentiate with dimension */ #define P8EST_DIM_POW(a) ((a) * (a) * (a)) -/* size of face transformation encoding */ +/** size of face transformation encoding */ #define P8EST_FTRANSFORM 9 /** p8est identification string */ #define P8EST_STRING "p8est" -/* Increase this number whenever the on-disk format for +/** Increase this number whenever the on-disk format for * p8est_connectivity, p8est, or any other 3D data structure changes. * The format for reading and writing must be the same. */ @@ -89,10 +118,12 @@ SC_EXTERN_C_BEGIN; typedef enum { /* make sure to have different values 2D and 3D */ - P8EST_CONNECT_FACE = 31, - P8EST_CONNECT_EDGE = 32, - P8EST_CONNECT_CORNER = 33, - P8EST_CONNECT_FULL = P8EST_CONNECT_CORNER + P8EST_CONNECT_SELF = 30, /**< No balance whatsoever. */ + P8EST_CONNECT_FACE = 31, /**< Balance across faces only. */ + P8EST_CONNECT_EDGE = 32, /**< Balance across faces and edges. */ + P8EST_CONNECT_ALMOST = P8EST_CONNECT_EDGE, /**< = CORNER - 1. */ + P8EST_CONNECT_CORNER = 33, /**< Balance faces, edges, corners. */ + P8EST_CONNECT_FULL = P8EST_CONNECT_CORNER /**< = CORNER. */ } p8est_connect_type_t; @@ -128,6 +159,7 @@ const char *p8est_connect_type_string (p8est_connect_type_t btype); * For faces the order is -x +x -y +y -z +z. * They are allocated [0][0]..[0][N-1]..[num_trees-1][0]..[num_trees-1][N-1]. * where N is 6 for tree and face, 8 for corner, 12 for edge. + * If a face is on the physical boundary it must connect to itself. * * The values for tree_to_face are in 0..23 * where ttf % 6 gives the face number and ttf / 6 the face orientation code. @@ -142,8 +174,10 @@ const char *p8est_connect_type_string (p8est_connect_type_t btype); * In this case vertices and tree_to_vertex are set to NULL. * Otherwise the vertex coordinates are stored in the array vertices as * [0][0]..[0][2]..[num_vertices-1][0]..[num_vertices-1][2]. + * Vertex coordinates are optional and not used for inferring topology. * - * The edges are only stored when they connect trees. + * The edges are stored when they connect trees that are not already face + * neighbors at that specific edge. * In this case tree_to_edge indexes into \a ett_offset. * Otherwise the tree_to_edge entry must be -1 and this edge is ignored. * If num_edges == 0, tree_to_edge and edge_to_* arrays are set to NULL. @@ -156,7 +190,8 @@ const char *p8est_connect_type_string (p8est_connect_type_t btype); * The edge_to_edge array holds values in 0..23, where the lower 12 indicate * one edge orientation and the higher 12 the opposite edge orientation. * - * The corners are only stored when they connect trees. + * The corners are stored when they connect trees that are not already edge + * or face neighbors at that specific corner. * In this case tree_to_corner indexes into \a ctt_offset. * Otherwise the tree_to_corner entry must be -1 and this corner is ignored. * If num_corners == 0, tree_to_corner and corner_to_* arrays are set to NULL. @@ -168,6 +203,13 @@ const char *p8est_connect_type_string (p8est_connect_type_t btype); * The size of the corner_to_* arrays is num_ctt = ctt_offset[num_corners]. * * The *_to_attr arrays may have arbitrary contents defined by the user. + * + * \note If a connectivity implies natural connections between trees that + * are edge neighbors without being face neighbors, these edges shall be + * encoded explicitly in the connectivity. If a connectivity implies + * natural connections between trees that are corner neighbors without being + * edge or face neighbors, these corners shall be encoded explicitly in the + * connectivity. */ typedef struct p8est_connectivity { @@ -217,34 +259,107 @@ p8est_connectivity_t; size_t p8est_connectivity_memory_used (p8est_connectivity_t * conn); +/** Generic interface for transformations between a tree and any of its edge */ typedef struct { - p4est_topidx_t ntree; - int8_t nedge, naxis[3], nflip, corners; + p4est_topidx_t ntree; /**< The number of the tree*/ + int8_t nedge; /**< The number of the edge*/ + int8_t naxis[3]; /**< The 3 edge coordinate axes*/ + int8_t nflip; /**< The orientation of the edge*/ + int8_t corners; /**< The corners connected to the edge*/ } p8est_edge_transform_t; +/** Information about the neighbors of an edge*/ typedef struct { - int8_t iedge; - sc_array_t edge_transforms; + int8_t iedge; /**< The information of the edge*/ + sc_array_t edge_transforms; /**< The array of neighbors of the originating + edge */ } p8est_edge_info_t; +/** Generic interface for transformations between a tree and any of its corner */ typedef struct { - p4est_topidx_t ntree; - int8_t ncorner; + p4est_topidx_t ntree; /**< The number of the tree*/ + int8_t ncorner; /**< The number of the corner*/ } p8est_corner_transform_t; +/** Information about the neighbors of a corner*/ typedef struct { - p4est_topidx_t icorner; - sc_array_t corner_transforms; + p4est_topidx_t icorner; /**< The number of the originating corner */ + sc_array_t corner_transforms; /**< The array of neighbors of the originating + corner */ } p8est_corner_info_t; +/** Generic interface for transformations between a tree and any of its neighbors */ +typedef struct +{ + p8est_connect_type_t neighbor_type; /**< type of connection to neighbor*/ + p4est_topidx_t neighbor; /**< neighbor tree index */ + int8_t index_self; /**< index of interface from self's + perspective */ + int8_t index_neighbor; /**< index of interface from neighbor's + perspective */ + int8_t perm[P8EST_DIM]; /**< permutation of dimensions when + transforming self coords to + neighbor coords */ + int8_t sign[P8EST_DIM]; /**< sign changes when transforming self + coords to neighbor coords */ + p4est_qcoord_t origin_self[P8EST_DIM]; /**< point on the interface from + self's perspective */ + p4est_qcoord_t origin_neighbor[P8EST_DIM]; /**< point on the interface + from neighbor's + perspective */ +} +p8est_neighbor_transform_t; + +/* *INDENT-OFF* */ + +/** Transform from self's coordinate system to neighbor's coordinate system. + * + * \param [in] nt A neighbor transform. + * \param [in] self_coords Input quadrant coordinates in self coordinates. + * \param [out] neigh_coords Coordinates transformed into neighbor coordinates. + */ +void p8est_neighbor_transform_coordinates + (const p8est_neighbor_transform_t * nt, + const p4est_qcoord_t self_coords[P8EST_DIM], + p4est_qcoord_t neigh_coords[P8EST_DIM]); + +/** Transform from neighbor's coordinate system to self's coordinate system. + * + * \param [in] nt A neighbor transform. + * \param [in] neigh_coords Input quadrant coordinates in self coordinates. + * \param [out] self_coords Coordinates transformed into neighbor coordinates. + */ +void p8est_neighbor_transform_coordinates_reverse + (const p8est_neighbor_transform_t * nt, + const p4est_qcoord_t neigh_coords[P8EST_DIM], + p4est_qcoord_t self_coords[P8EST_DIM]); + +/** Fill an array with the neighbor transforms based on a specific boundary type. + * This function generalizes all other inter-tree transformation objects + * + * \param [in] conn Connectivity structure. + * \param [in] tree_id The number of the tree. + * \param [in] boundary_type Type of boundary connection (self, face, edge, corner). + * \param [in] boundary_index The index of the boundary. + * \param [in,out] neighbor_transform_array Array of the neighbor transforms. + */ +void p8est_connectivity_get_neighbor_transforms + (p8est_connectivity_t *conn, + p4est_topidx_t tree_id, + p8est_connect_type_t boundary_type, + int boundary_index, + sc_array_t *neighbor_transform_array); + +/* *INDENT-ON* */ + /** Store the corner numbers 0..7 for each tree face. */ extern const int p8est_face_corners[6][4]; @@ -319,7 +434,7 @@ extern const int p8est_child_corner_edges[8][8]; * It expects a face permutation index that has been precomputed. * \param [in] c A corner number in 0..7. * \param [in] f A face number that touches the corner \a c. - * \param [in] nf A neighbor face that is on the other side of \f. + * \param [in] nf A neighbor face that is on the other side of \a f. * \param [in] set A value from \a p8est_face_permutation_sets that is * obtained using \a f, \a nf, and a valid orientation: * ref = p8est_face_permutation_refs[f][nf]; @@ -333,8 +448,8 @@ int p8est_connectivity_face_neighbor_corner_set * This version expects the neighbor face and orientation separately. * \param [in] fc A face corner number in 0..3. * \param [in] f A face that the face corner \a fc is relative to. - * \param [in] nf A neighbor face that is on the other side of \f. - * \param [in] o The orientation between tree boundary faces \a f and \nf. + * \param [in] nf A neighbor face that is on the other side of \a f. + * \param [in] o The orientation between tree boundary faces \a f and \a nf. * \return The face corner number relative to the neighbor's face. */ int p8est_connectivity_face_neighbor_face_corner @@ -344,8 +459,8 @@ int p8est_connectivity_face_neighbor_face_corner * This version expects the neighbor face and orientation separately. * \param [in] c A corner number in 0..7. * \param [in] f A face number that touches the corner \a c. - * \param [in] nf A neighbor face that is on the other side of \f. - * \param [in] o The orientation between tree boundary faces \a f and \nf. + * \param [in] nf A neighbor face that is on the other side of \a f. + * \param [in] o The orientation between tree boundary faces \a f and \a nf. * \return The number of the corner seen from the neighbor tree. */ int p8est_connectivity_face_neighbor_corner @@ -355,8 +470,8 @@ int p8est_connectivity_face_neighbor_corner * This version expects the neighbor face and orientation separately. * \param [in] fe A face edge number in 0..3. * \param [in] f A face number that touches the edge \a e. - * \param [in] nf A neighbor face that is on the other side of \f. - * \param [in] o The orientation between tree boundary faces \a f and \nf. + * \param [in] nf A neighbor face that is on the other side of \a f. + * \param [in] o The orientation between tree boundary faces \a f and \a nf. * \return The face edge number seen from the neighbor tree. */ int p8est_connectivity_face_neighbor_face_edge @@ -366,8 +481,8 @@ int p8est_connectivity_face_neighbor_face_edge * This version expects the neighbor face and orientation separately. * \param [in] e A edge number in 0..11. * \param [in] f A face 0..5 that touches the edge \a e. - * \param [in] nf A neighbor face that is on the other side of \f. - * \param [in] o The orientation between tree boundary faces \a f and \nf. + * \param [in] nf A neighbor face that is on the other side of \a f. + * \param [in] o The orientation between tree boundary faces \a f and \a nf. * \return The edge's number seen from the neighbor. */ int p8est_connectivity_face_neighbor_edge @@ -385,8 +500,8 @@ int p8est_connectivity_edge_neighbor_edge_corner * This version expects the neighbor edge and orientation separately. * \param [in] c A corner number in 0..7. * \param [in] e An edge 0..11 that touches the corner \a c. - * \param [in] ne A neighbor edge that is on the other side of \e. - * \param [in] o The orientation between tree boundary edges \a e and \ne. + * \param [in] ne A neighbor edge that is on the other side of \a e. + * \param [in] o The orientation between tree boundary edges \a e and \a ne. * \return Corner number seen from the neighbor. */ int p8est_connectivity_edge_neighbor_corner @@ -415,12 +530,22 @@ p8est_connectivity_t *p8est_connectivity_new (p4est_topidx_t num_vertices, * \param [in] num_trees Number of trees in the forest. * \param [in] num_edges Number of tree-connecting edges. * \param [in] num_corners Number of tree-connecting corners. + * \param [in] vertices Coordinates of the vertices of the trees. + * \param [in] ttv The tree-to-vertex array. + * \param [in] ttt The tree-to-tree array. + * \param [in] ttf The tree-to-face array (int8_t). + * \param [in] tte The tree-to-edge array. * \param [in] eoff Edge-to-tree offsets (num_edges + 1 values). * This must always be non-NULL; in trivial cases * it is just a pointer to a p4est_topix value of 0. + * \param [in] ett The edge-to-tree array. + * \param [in] ete The edge-to-edge array. + * \param [in] ttc The tree-to-corner array. * \param [in] coff Corner-to-tree offsets (num_corners + 1 values). * This must always be non-NULL; in trivial cases * it is just a pointer to a p4est_topix value of 0. + * \param [in] ctt The corner-to-tree array. + * \param [in] ctc The corner-to-corner array. * \return The connectivity is checked for validity. */ p8est_connectivity_t *p8est_connectivity_new_copy (p4est_topidx_t @@ -530,8 +655,10 @@ int p8est_connectivity_save (const char *filename, p8est_connectivity_t *p8est_connectivity_source (sc_io_source_t * source); /** Create new connectivity from a memory buffer. + * This function aborts on malloc errors. * \param [in] buffer The connectivity is created from this memory buffer. - * \return The newly created connectivity, or NULL on error. + * \return The newly created connectivity, or NULL on format + * error of the buffered connectivity data. */ p8est_connectivity_t *p8est_connectivity_inflate (sc_array_t * buffer); @@ -557,6 +684,12 @@ p8est_connectivity_t *p8est_connectivity_new_periodic (void); */ p8est_connectivity_t *p8est_connectivity_new_rotwrap (void); +/** Create a connectivity structure for a five-trees geometry with a + * hole. The geometry is a 3D extrusion of the two drop example, and + * covers [0, 3]*[0, 2]*[0, 3]. The additional dimension is Y. + */ +p8est_connectivity_t *p8est_connectivity_new_drop (void); + /** Create a connectivity structure that contains two cubes. */ p8est_connectivity_t *p8est_connectivity_new_twocubes (void); @@ -609,7 +742,7 @@ p8est_connectivity_t *p8est_connectivity_new_sphere (void); * It is thus not suitable for p8est_connectivity_complete. * * This connectivity reuses ideas from disk2d connectivity. - * More precisely the torus is divided into segments arround + * More precisely the torus is divided into segments around * the revolution axis, each segments is made of 5 trees (à la disk2d). * The total number of trees if 5 times the number of segments. * @@ -639,13 +772,13 @@ p8est_connectivity_t *p8est_connectivity_new_byname (const char *name); * than a power of 2. * * \param [in] conn A valid connectivity - * \param [in] num_per_edge The number of new trees in each direction. + * \param [in] num_per_dim The number of new trees in each direction. * Must use no more than \ref P8EST_OLD_QMAXLEVEL bits. * * \return a refined connectivity. */ p8est_connectivity_t *p8est_connectivity_refine (p8est_connectivity_t * conn, - int num_per_edge); + int num_per_dim); /** Fill an array with the axis combination of a face neighbor transform. * \param [in] iface The number of the originating face. @@ -669,12 +802,14 @@ void p8est_expand_face_transform (int iface, int nface, int ftransform[]); /** Fill an array with the axis combination of a face neighbor transform. + * \param [in] connectivity Connectivity structure. * \param [in] itree The number of the originating tree. * \param [in] iface The number of the originating tree's face. * \param [out] ftransform This array holds 9 integers. * [0]..[2] The coordinate axis sequence of the origin face. * [3]..[5] The coordinate axis sequence of the target face. - * [6]..[8] Edge reverse flag for axes t1, t2; face code for n. + * [6]..[8] Edge reversal flag for axes t1, t2; face code for n; + * \see p8est_expand_face_transform. * \return The face neighbor tree if it exists, -1 otherwise. */ p4est_topidx_t p8est_find_face_transform (p8est_connectivity_t * @@ -683,6 +818,7 @@ p4est_topidx_t p8est_find_face_transform (p8est_connectivity_t * int iface, int ftransform[]); /** Fills an array with information about edge neighbors. + * \param [in] connectivity Connectivity structure. * \param [in] itree The number of the originating tree. * \param [in] iedge The number of the originating edge. * \param [in,out] ei A p8est_edge_info_t structure with initialized array. @@ -694,6 +830,7 @@ void p8est_find_edge_transform (p8est_connectivity_t * p8est_edge_info_t * ei); /** Fills an array with information about corner neighbors. + * \param [in] connectivity Connectivity structure. * \param [in] itree The number of the originating tree. * \param [in] icorner The number of the originating corner. * \param [in,out] ci A p8est_corner_info_t structure with initialized array. diff --git a/src/p8est_extended.h b/src/p8est_extended.h index 960610e53..680cab5a7 100644 --- a/src/p8est_extended.h +++ b/src/p8est_extended.h @@ -40,11 +40,11 @@ #ifndef P8EST_EXTENDED_H #define P8EST_EXTENDED_H -#include +#include #include #include #include -#include +#include SC_EXTERN_C_BEGIN; @@ -140,24 +140,24 @@ int p8est_lid_is_equal (const p8est_lid_t * a, /** Initializes a linear index to a given value. * \param [in,out] a A pointer to the p8est_lid_t that will be * initialized. - * \param [in] high The given high bits to intialize \a a. + * \param [in] high The given high bits to initialize \a a. * \param [in] low The given low bits to initialize \a a. */ void p8est_lid_init (p8est_lid_t * input, uint64_t high, uint64_t low); /** Initializes a linear index to zero. - * \param [out] input A pointer to a p4est_lid_t that will be intialized. + * \param [out] input A pointer to a p4est_lid_t that will be initialized. */ void p8est_lid_set_zero (p8est_lid_t * input); /** Initializes a linear index to one. - * \param [out] input A pointer to a p4est_lid_t that will be intialized. + * \param [out] input A pointer to a p4est_lid_t that will be initialized. */ void p8est_lid_set_one (p8est_lid_t * input); /** Initializes a linear index to an unsigned 64 bit integer. - * \param [out] input A pointer to a p4est_lid_t that will be intialized. + * \param [out] input A pointer to a p4est_lid_t that will be initialized. */ void p8est_lid_set_uint64 (p8est_lid_t * input, uint64_t u); @@ -203,7 +203,7 @@ void p8est_lid_add (const p8est_lid_t * a, const p8est_lid_t * b, p8est_lid_t * result); -/** Substracts the p8est_lid_t \a b from the p8est_lid_t \a a. +/** Subtracts the p8est_lid_t \a b from the p8est_lid_t \a a. * This function assumes that the result is >= 0. * \a result == \a a or \a result == \a b is not allowed. * \a a == \a b is allowed. @@ -289,7 +289,7 @@ void p8est_lid_shift_left (const p8est_lid_t * input, void p8est_lid_add_inplace (p8est_lid_t * a, const p8est_lid_t * b); -/** Substracts the uint128_t \a b from the uint128_t \a a. +/** Subtracts the uint128_t \a b from the uint128_t \a a. * The result is saved in \a a. \a a == \a b is allowed. * This function assumes that the result is >= 0. * \param [in,out] a A pointer to a p8est_lid_t. @@ -386,6 +386,8 @@ p8est_t *p8est_new_ext (sc_MPI_Comm mpicomm, void *user_pointer); /** Create a new mesh. + * This function sets a subset of the mesh creation parameters. For full control + * use \ref p8est_mesh_new_params. * \param [in] p8est A forest that is fully 2:1 balanced. * \param [in] ghost The ghost layer created from the * provided p4est. @@ -393,11 +395,11 @@ p8est_t *p8est_new_ext (sc_MPI_Comm mpicomm, * compute the quad_to_tree list. * \param [in] compute_level_lists Boolean to decide whether to compute the * level lists in quad_level. - * \param [in] btype Currently ignored, only face neighbors - * are stored. + * \param [in] btype Flag indicating the connection types (face, + edge, corner) stored in the mesh. * \return A fully allocated mesh structure. */ -p8est_mesh_t *p8est_mesh_new_ext (p8est_t * p4est, +p8est_mesh_t *p8est_mesh_new_ext (p8est_t * p8est, p8est_ghost_t * ghost, int compute_tree_index, int compute_level_lists, @@ -596,6 +598,41 @@ p8est_t *p8est_source_ext (sc_io_source_t * src, int broadcasthead, void *user_pointer, p8est_connectivity_t ** connectivity); +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +/** Open a file for reading without knowing the p4est that is associated + * with the mesh-related data in the file (cf. \ref p8est_file_open_read). + * For more general comments on open_read see the documentation of + * \ref p8est_file_open_read. + * The parameters that are not documented are the same as in \ref + * p8est_file_open_read. + * + * \param [in] mpicomm The MPI communicator that is used to read the file. + */ +p8est_file_context_t *p8est_file_open_read_ext (sc_MPI_Comm mpicomm, + const char *filename, + char *user_string, + p4est_gloidx_t * + global_num_quadrants, + int *errcode); + +/** Read a data field and specify the partition for reading in parallel. + * See also the documentation of \ref p8est_file_read_field. + * + * \param [in] gfq An array of the size mpisize + 1 that contains the global + * first quadrants per rank and + * gfq[mpisize] == global_num_quadrants. This defines + * partition that is used to read the data field in parallel. + */ +p8est_file_context_t *p8est_file_read_field_ext (p8est_file_context_t * fc, + p4est_gloidx_t * gfq, + size_t quadrant_size, + sc_array_t * quadrant_data, + char *user_string, + int *errcode); + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + /** Create the data necessary to create a PETsc DMPLEX representation of a * forest, as well as the accompanying lnodes and ghost layer. The forest * must be at least face balanced (see p4est_balance()). See diff --git a/src/p8est_geometry.c b/src/p8est_geometry.c index 4be5ee5b8..a600c4cdb 100644 --- a/src/p8est_geometry.c +++ b/src/p8est_geometry.c @@ -96,7 +96,7 @@ p8est_geometry_shell_X (p8est_geometry_t * geom, double abc[3]; /* transform from the reference cube into vertex space */ - p4est_geometry_connectivity_X (geom, which_tree, rst, abc); + p8est_geometry_connectivity_X (geom, which_tree, rst, abc); /* assert that input points are in the expected range */ P4EST_ASSERT (shell->type == P8EST_GEOMETRY_BUILTIN_SHELL); @@ -184,7 +184,7 @@ p8est_geometry_sphere_X (p8est_geometry_t * geom, double abc[3]; /* transform from the reference cube into vertex space */ - p4est_geometry_connectivity_X (geom, which_tree, rst, abc); + p8est_geometry_connectivity_X (geom, which_tree, rst, abc); /* assert that input points are in the expected range */ P4EST_ASSERT (sphere->type == P8EST_GEOMETRY_BUILTIN_SPHERE); @@ -341,7 +341,7 @@ p8est_geometry_torus_X (p8est_geometry_t * geom, /* transform from the reference cube [0,1]^3 into logical vertex space using bi/trilinear transformation */ - p4est_geometry_connectivity_X (geom, which_tree, rst, abc); + p8est_geometry_connectivity_X (geom, which_tree, rst, abc); /* * assert that input points are in the expected range @@ -352,7 +352,6 @@ p8est_geometry_torus_X (p8est_geometry_t * geom, p4est_topidx_t which_tree_local = which_tree % 5; P4EST_ASSERT (torus->type == P8EST_GEOMETRY_BUILTIN_TORUS); - //P4EST_ASSERT (0 <= which_tree && which_tree < 5); P4EST_ASSERT (abc[0] < 1.0 + SC_1000_EPS && abc[0] > -1.0 - SC_1000_EPS); if (which_tree_local < 4) P4EST_ASSERT (abc[1] < 2.0 + SC_1000_EPS && abc[1] > 1.0 - SC_1000_EPS); @@ -373,7 +372,7 @@ p8est_geometry_torus_X (p8est_geometry_t * geom, R = torus->R0sqrbyR1 * pow (torus->R1byR0, abc[1]); /* R*cos(theta) */ - //q = R / sqrt (x * x + 1.); + /* q = R / sqrt (x * x + 1.); */ q = R / sqrt (1. + (1. - p) * (tanx * tanx) + 1. * p); /* assign correct coordinates based on patch id */ @@ -408,7 +407,7 @@ p8est_geometry_torus_X (p8est_geometry_t * geom, } - // translation + /* translation */ xyz[0] += torus->R2; /* rotate around Y-axis */ @@ -448,7 +447,7 @@ p8est_geometry_new_torus (p8est_connectivity_t * conn, /* variables useful for the center square */ torus->Clength = R0 / sqrt (2.); - //torus->CdetJ = pow (R0 / sqrt (3.), 3.); + /* torus->CdetJ = pow (R0 / sqrt (3.), 3.); */ builtin->geom.name = "p8est_torus"; builtin->geom.user = conn; diff --git a/src/p8est_geometry.h b/src/p8est_geometry.h index 5dc9f3ae7..d2828899c 100644 --- a/src/p8est_geometry.h +++ b/src/p8est_geometry.h @@ -22,7 +22,15 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** \file p8est_geometry.h transforms from vertex frame to physical space. +/** \file p8est_geometry.h Transform from tree-local "reference" coordinate system + * to global "physical space" coordinates. These are used in \ref p8est_vtk.h to write + * global coordinate meshes to disk. + * + * We provide several example geometries for use. You may also implement your own + * geometry as you see fit. + * + * \note For geometry purposes, each tree has the local coordinate system + * \f$[0,1]^3\f$. * * \ingroup p8est */ @@ -47,7 +55,7 @@ typedef void (*p8est_geometry_X_t) (p8est_geometry_t * geom, /** Destructor prototype for a user-allocated \a p8est_geometry_t. * It is invoked by p8est_geometry_destroy. If the user chooses to - * reserve the structure statically, simply don't call p4est_geometry_destroy. + * reserve the structure statically, there is no need to provide it. */ typedef void (*p8est_geometry_destroy_t) (p8est_geometry_t * geom); @@ -66,7 +74,7 @@ struct p8est_geometry /** Can be used to conveniently destroy a geometry structure. * The user is free not to call this function at all if they handle the - * memory of the p8est_geometry_t in their own way. + * memory of the \ref p8est_geometry_t in their own way. */ void p8est_geometry_destroy (p8est_geometry_t * geom); @@ -74,17 +82,37 @@ void p8est_geometry_destroy (p8est_geometry_t * geom); * The transformation is constructed using trilinear interpolation. * \param [in] conn A p8est_connectivity_t with valid vertices. We do NOT * take ownership and expect this structure to stay alive. - * \return Geometry structure; use with p4est_geometry_destroy. + * \return Geometry structure; use with \ref p4est_geometry_destroy. */ p8est_geometry_t *p8est_geometry_new_connectivity (p8est_connectivity_t * conn); +/** Geometric coordinate transformation for geometry created with + * \ref p8est_geometry_new_connectivity. This is defined by + * tri/binlinear interpolation from vertex coordinates. + * + * May also be used as a building block in custom geometric coordinate transforms. + * See for example \ref p8est_geometry_shell_X or \ref p8est_geometry_sphere_X. + * + * \param[in] geom associated geometry + * \param[in] which_tree tree id inside forest + * \param[in] abc tree-local reference coordinates : [0,1]^3. + * \param[out] xyz Cartesian coordinates in physical space after geometry + * + * \warning The associated geometry is assumed to have a connectivity + * as its *user field, and this connectivity is assumed to have vertex + * information in its *tree_to_vertex field. + */ +void p8est_geometry_connectivity_X (p8est_geometry_t * geom, + p4est_topidx_t which_tree, + const double abc[3], double xyz[3]); + /** Create a geometry structure for the spherical shell of 24 trees. * \param [in] conn Result of p8est_connectivity_new_shell or equivalent. * We do NOT take ownership and expect it to stay alive. * \param [in] R2 The outer radius of the shell. * \param [in] R1 The inner radius of the shell. - * \return Geometry structure; use with p4est_geometry_destroy. + * \return Geometry structure; use with \ref p4est_geometry_destroy. */ p8est_geometry_t *p8est_geometry_new_shell (p8est_connectivity_t * conn, double R2, double R1); @@ -95,7 +123,7 @@ p8est_geometry_t *p8est_geometry_new_shell (p8est_connectivity_t * conn, * \param [in] R2 The outer radius of the sphere. * \param [in] R1 The outer radius of the inner shell. * \param [in] R0 The inner radius of the inner shell. - * \return Geometry structure; use with p4est_geometry_destroy. + * \return Geometry structure; use with \ref p4est_geometry_destroy. */ p8est_geometry_t *p8est_geometry_new_sphere (p8est_connectivity_t * conn, double R2, double R1, @@ -106,7 +134,7 @@ p8est_geometry_t *p8est_geometry_new_sphere (p8est_connectivity_t * conn, * This geometry maps a revolution torus, obtained using * \ref p8est_connectivity_new_torus * - * The torus is divided into into segments arround the revolution axis, + * The torus is divided into into segments around the revolution axis, * each segments is made of 5 trees; so here we provided the geometric * transformation in a piecewise manner for each tree of the connectivity. * @@ -116,7 +144,7 @@ p8est_geometry_t *p8est_geometry_new_sphere (p8est_connectivity_t * conn, * \param [in] R0 The inner radius of the 2d disk slice. * \param [in] R1 The outer radius of the 2d disk slice. * \param [in] R2 The outer radius of the torus. - * \return Geometry structure; use with p4est_geometry_destroy. + * \return Geometry structure; use with \ref p4est_geometry_destroy. */ p8est_geometry_t *p8est_geometry_new_torus (p8est_connectivity_t * conn, double R0, double R1, diff --git a/src/p8est_ghost.h b/src/p8est_ghost.h index 0dee9d9e4..738b47b02 100644 --- a/src/p8est_ghost.h +++ b/src/p8est_ghost.h @@ -77,7 +77,7 @@ typedef struct } p8est_ghost_t; -/** Examine if a ghost structure is valid as desribed above. +/** Examine if a ghost structure is valid as described above. * Test if within a ghost-structure the arrays ghosts and mirrors are in * p8est_quadrant_compare_piggy order. * Test if local_num in piggy3 data member of the quadrants in ghosts and @@ -130,6 +130,17 @@ int p8est_quadrant_find_owner (p8est_t * p8est, p8est_ghost_t *p8est_ghost_new (p8est_t * p8est, p8est_connect_type_t btype); +/** Generate an empty ghost layer. + * This ghost layer pretends that there are no parallel neighbor elements. + * It is useful if general algorithms should be run with local data only. + * \param [in] p8est Valid forest. + * \param [in] ctype Ghosts to include (none, across face, face/corner). + * This variable must be valid but has no effect. + * \return Valid ghost layer of zero ghost elements. + */ +p8est_ghost_t *p8est_ghost_new_local (p8est_t * p8est, + p8est_connect_type_t ctype); + /** Frees all memory used for the ghost layer. */ void p8est_ghost_destroy (p8est_ghost_t * ghost); diff --git a/src/p8est_io.h b/src/p8est_io.h index 028e8ee6e..c60e2f27f 100644 --- a/src/p8est_io.h +++ b/src/p8est_io.h @@ -22,6 +22,16 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/** \file p8est_io.h + * + * Provide functions to serialize/deserialize a forest. + * Some are used as building blocks for \ref p8est_load and \ref p8est_save. + * Others allow for saving and loading user-defined data to a parallel file. + * + * Furthermore, this module provides functions to write and read general data + * files associated with a p8est. + */ + #ifndef P8EST_IO_H #define P8EST_IO_H @@ -29,6 +39,41 @@ SC_EXTERN_C_BEGIN; +/** This parallel data file format is deprecated since we plan to release an + * updated version of it soon. You can still use \ref p8est_load and \ref + * p8est_save to read and write a p8est including the connectivity and + * quadrant data. However, you can not read and write external mesh associated + * data using a p8est function if you do not use the p4est_file functions. + * If you still want to use the p8est_file functions you can configure + * with --enable-file-deprecated or use the variable enable-file-deprecated + * in CMake. +*/ +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +#define P8EST_FILE_MAGIC_NUMBER "p8data0" /**< magic string for p8est data files */ +#define P8EST_FILE_METADATA_BYTES 96 /**< number of file metadata bytes */ +#define P8EST_FILE_MAGIC_BYTES 8 /**< number of bytes of the magic number without \n */ +#define P8EST_FILE_VERSION_STR_BYTES 24 /**< number of bytes of the version str. without \n */ +#define P8EST_FILE_ARRAY_METADATA_BYTES 14 /**< number of array metadata bytes */ +/* subtract 2 for '\n' at the beginning and end of the array metadata */ +#define P8EST_FILE_ARRAY_METADATA_CHARS (P8EST_FILE_ARRAY_METADATA_BYTES - 2) /**< number of array metadata chars */ +#define P8EST_FILE_BYTE_DIV 16 /**< All data blocks are padded to be divisible by this. */ +#define P8EST_FILE_MAX_NUM_PAD_BYTES (P8EST_FILE_BYTE_DIV + 1) /**< We enforce to pad in any + case and the padding string + needs to contain two + newline characters and + therefore this is the + maximal number of pad + bytes. */ +#define P8EST_FILE_USER_STRING_BYTES 48 /**< number of user string bytes */ +#define P8EST_FILE_FIELD_HEADER_BYTES (2 + P8EST_FILE_ARRAY_METADATA_BYTES + P8EST_FILE_USER_STRING_BYTES) + /**< number of bytes of one field header */ +#define P8EST_FILE_MAX_GLOBAL_QUAD 9999999999999999 /**< maximal number of global quadrants */ +#define P8EST_FILE_MAX_BLOCK_SIZE 9999999999999 /**< maximal number of block bytes */ +#define P8EST_FILE_MAX_FIELD_ENTRY_SIZE 9999999999999 /**< maximal number of bytes per field entry */ + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + /** Extract processor local quadrants' x y z level data. * Optionally extracts the quadrant data as well into a separate array. * \param [in] p8est The forest is not modified. @@ -68,6 +113,641 @@ p8est_t *p8est_inflate (sc_MPI_Comm mpicomm, sc_array_t * quadrants, sc_array_t * data, void *user_pointer); +/** Create a new p4est based on serialized data. + * Its revision counter is set to zero. + * See p8est.h and p8est_communication.h for more information on parameters. + * In contrast to \ref p8est_inflate this function indicates soft errors + * by returning NULL. + * \param [in] mpicomm A valid MPI communicator. + * \param [in] connectivity This is the connectivity information that + * the forest is built with. Note that p4est + * does not take ownership of the memory. + * \param [in] global_first_quadrant First global quadrant on each proc and + * one beyond. Copied into global_first_quadrant. + * Local count on rank is gfq[rank + 1] - gfq[rank]. + * \param [in] pertree The cumulative quadrant counts per tree. + * \param [in] quadrants Array as returned by p8est_deflate_quadrants. + * \param [in] data Array as from p8est_deflate_quadrants or NULL. + * The elem_size of this array informs data_size. + * Its elem_count equals the number of local quads. + * \param [in] user_pointer Assign to the user_pointer member of the p4est. + * \return The newly created p4est with a zero revision counter. + * If the created p4est would not be valid, no p4est + * is created and the function returns NULL. + */ +p8est_t *p8est_inflate_null (sc_MPI_Comm mpicomm, + p8est_connectivity_t * connectivity, + const p4est_gloidx_t * + global_first_quadrant, + const p4est_gloidx_t * pertree, + sc_array_t * quadrants, + sc_array_t * data, + void *user_pointer); + +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +/** p8est data file format + * All p4est data files have 64 bytes file header section at the beginning of the file. + * The file header section is written to the file as string without NUL-termination + * (called string*) and is therefore readable in a text editor. + * + * File Header (96 bytes): + * 7 bytes magic number (p8data0) and 1 byte new line char. + * 23 bytes p4est version string* and 1 byte new line char. + * 47 bytes user string* and 1 byte new line char. + * 16 bytes number of global quadrants. + * + * The file header section is padded by 16 bytes consisting of 1 byte + * new line char succeeded by 14 bytes of spaces and 1 trailing byte + * new line char. + * + * The actual data is stored in arrays corresponding to a mesh of a p4est + * or in block sections that have a fixed user-defined size. The block + * sections are written and read on rank 0. + * One data field stores a fixed number of bytes of user- + * defined data per quadrant of a certain p4est. Therefore, one user-defined + * data field is of the size p4est->global_num_quadrants * data_size, where + * data_size is set by the user. The file format is partition independent. + * The data fields are padded such that the number of bytes for + * an array is divisible by 16. The padding also enforced for data blocks + * that have a size that is divisble by 16. + * The p4est data file consists of a variable number (including 0) of + * these two types of data sections. + * Every data section includes 64 bytes of section header written at the beginning + * by p4est. These 64 bytes are again written to the file as string* and can + * be read using a text editor. + * + * Data section Header (64 bytes): + * One byte data section type specific character (B for a block section and F for + * a data field), 1 byte space and 13 bytes size in number of bytes for a + * block section and data size per element in byte for a field section + * and one trailing byte new line char. + * 47 bytes user-defined string* and 1 byte new line char. + * + * The structure of p4est and p8est data files differs only by the magic number. + * + * The p4est metadata of a p4est data file can be accessed by \ref p8est_file_info(). + */ + +/** Opaque context used for writing a p8est data file. */ +typedef struct p8est_file_context p8est_file_context_t; + +/** Error values for p4est_file functions. + */ +typedef enum p8est_file_error +{ + P8EST_FILE_ERR_SUCCESS = sc_MPI_ERR_LASTCODE, /**< file function completed with success */ + P8EST_FILE_ERR_FILE, /**< invalid file handle */ + P8EST_FILE_ERR_NOT_SAME, /**< collective arg not identical */ + P8EST_FILE_ERR_AMODE, /**< access mode error */ + P8EST_FILE_ERR_NO_SUCH_FILE, /**< file does not exist */ + P8EST_FILE_ERR_FILE_EXIST, /**< file exists already */ + P8EST_FILE_ERR_BAD_FILE, /**< invalid file name */ + P8EST_FILE_ERR_ACCESS, /**< permission denied */ + P8EST_FILE_ERR_NO_SPACE, /**< not enough space */ + P8EST_FILE_ERR_QUOTA, /**< quota exceeded */ + P8EST_FILE_ERR_READ_ONLY, /**< read only file (system) */ + P8EST_FILE_ERR_IN_USE, /**< file currently open by other process */ + P8EST_FILE_ERR_IO, /**< other I/O error */ + P8EST_FILE_ERR_FORMAT, /**< read file has a wrong format */ + P8EST_FILE_ERR_SECTION_TYPE, /**< a valid non-matching section type */ + P8EST_FILE_ERR_CONN, /**< invalid serialized connectivity data */ + P8EST_FILE_ERR_P8EST, /**< invalid p8est data */ + P8EST_FILE_ERR_IN_DATA, /**< input data of file function is invalid */ + P8EST_FILE_ERR_COUNT, /**< read or write count error that was not + classified as a format error */ + P8EST_FILE_ERR_UNKNOWN, /**< unknown error */ + P8EST_FILE_ERR_LASTCODE /**< to define own error codes for + a higher level application + that is using p4est_file + functions */ +} +p8est_file_error_t; + +/** Begin writing file header and saving data blocks into a parallel file. + * + * This function creates a new file or overwrites an existing one. + * It is collective and creates the file on a parallel file system. + * It takes an (optional) pointer to write a header of given size. + * This function leaves the file open if MPI I/O is available. + * It is necessary to call \ref + * p8est_file_close (possibly after writing one or more data sets). + * The file is opened in a write-only mode. + * + * We add some basic metadata to the file. + * The file written contains the file header and data sets + * as specified by the open/write functions called. + * The file header consists of the metadata specified by p4est. + * + * The number of global quadrants must be less or equal + * \ref P8EST_FILE_MAX_GLOBAL_QUAD. + * + * It is the application's responsibility to write sufficient header + * information (cf. \ref p8est_file_write_block) to determine the number and + * size of the data sets if such information is not recorded and maintained + * externally. + * However, p4est makes some metadata accessible via + * \ref p8est_file_info. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in] p8est Valid forest. + * \param [in] filename Path to parallel file that is to be created. + * \param [in] user_string A user string that is written to the file header. + * Only \ref P8EST_FILE_USER_STRING_BYTES + * bytes without NUL-termination are + * written to the file. If the user gives less + * bytes the user_string in the file header is padded + * by spaces. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Newly allocated context to continue writing + * and eventually closing the file. NULL in + * case of error. + */ +p8est_file_context_t *p8est_file_open_create + (p8est_t * p8est, const char *filename, + const char *user_string, int *errcode); + +/** Open a file for reading and read its user string on rank zero. + * The user string is broadcasted to all ranks after reading. + * The file must exist and be at least of the size of the file header. + * + * If the file has wrong metadata the function reports the error using + * /ref P8EST_LERRORF, collectively close the file and deallocate + * the file context. In this case the function returns NULL on all ranks. + * The wrong file format or a wrong file header causes \ref P8EST_FILE_ERR_FORMAT + * as errcode. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in] p8est The forest must be of the same refinement + * pattern as the one used for writing the file. + * Its global number of quadrants must match. + * It is possible, however, to use a different + * partition or number of ranks from writing it. + * \param [in] filename The path to the file that is opened. + * \param [in,out] user_string At least \ref P8EST_FILE_USER_STRING_BYTES + * bytes. The user string is written + * to the passed array including padding spaces + * and a trailing NUL-termination. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Newly allocated context to continue reading + * and eventually closing the file. NULL in + * case of error. + */ +p8est_file_context_t *p8est_file_open_read (p8est_t * p8est, + const char *filename, + char *user_string, int *errcode); + +/** Write a block section to an opened file. + * This function requires an opened file context. + * The block data and its metadata are written on rank 0. + * The number of block bytes must be less or equal + * \ref P8EST_FILE_MAX_BLOCK_SIZE. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [out] fc Context previously created by \ref + * p8est_file_open_create. + * \param [in] block_size The size of block in bytes. + * May be equal to 0. In this case the + * section header and the padding + * is still written. + * This function returns the passed fc + * parameter and sets errcode to + * \ref P8EST_FILE_ERR_SUCCESS if it is called + * for block_size == 0. + * \param [in] block_data A sc_array with one element and element size + * equal to \a block_size. + * The array points to the block data. The user is + * responsible for the validality of the block + * data. block_data can be NULL if + * block_size == 0. + * \param [in] user_string Maximal \ref P8EST_FILE_USER_STRING_BYTES bytes. + * These chars are written to the block + * header and padded to + * \ref P8EST_FILE_USER_STRING_BYTES - 1 chars + * by adding spaces. The '\0' is not written + * to the file. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Return the input context to continue writing + * and eventually closing the file. The return + * value is NULL in case of error, then + * it also holds errcode != 0 and the file is + * tried to close and fc is freed. + */ +p8est_file_context_t *p8est_file_write_block (p8est_file_context_t * fc, + size_t block_size, + sc_array_t * block_data, + const char *user_string, + int *errcode); + +/** Read a header block from an opened file. + * This function requires an opened file context. + * The header data is read on rank 0. + * + * If the user does not have the header_size to call this function, the user + * can user \ref p8est_file_info to obtain the required information. + * + * The passed header_size is compared to the header_size stored in the file. + * If the values do not equal each other, the function reports details via + * /ref P8EST_LERRORF and closes and deallocate the file context. The return + * value in this case is NULL. + * If the block header information is not matching the passed parameters + * the function sets \ref P8EST_FILE_ERR_FORMAT for errcode. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [out] fc Context previously created by \ref + * p8est_file_open_create. + * \param [in] header_size The size of the header that is read. + * \param [in, out] header_data \a header_size allocated bytes in an sc_array + * with one element and \a header_size as element + * size. This data will be filled with the header + * data from file. If this is NULL it means that + * the current header block is skipped and the + * internal file pointer of the file context is + * set to the next data block. If current data + * block is not a header block, the file is closed + * and the file context is deallocated. Furthermore, + * in this case the function returns NULL and sets + * errcode to \ref P8EST_FILE_ERR_FORMAT. In case + * of skipping the header section \a header_size + * needs also to coincide with the header size + * given in the file. + * \param [in,out] user_string At least \ref P8EST_FILE_USER_STRING_BYTES bytes. + * Filled by the padded user string and + * a trailing NUL-termination char. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Return the input context to continue reading + * and eventually closing the file. The return value + * is NULL if the function was called for + * header_size == 0. The return + * value is also NULL in case of error but then + * it also holds errcode != 0 and the file is + * tried to close and fc is freed. + */ +p8est_file_context_t *p8est_file_read_block (p8est_file_context_t * fc, + size_t header_size, + sc_array_t * header_data, + char *user_string, int *errcode); + +/** Write one (more) per-quadrant data set to a parallel output file. + * + * This function requires an opened file context. + * The data set is appended to the header/previously written data sets. + * This function writes a block of the size number of quadrants * data_size. + * + * This function does not abort on MPI I/O errors but returns NULL. + * + * The number of bytes per field entry must be less or equal + * \ref P8EST_FILE_MAX_FIELD_ENTRY_SIZE. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [out] fc Context previously created by \ref + * p8est_file_open_create. + * \param [in] quadrant_size The number of bytes per quadrant. This number + * must coincide with \a quadrant_data->elem_size. + * \param [in] quadrant_data An array of the length number of local quadrants + * with the element size equal to number of bytes + * written per quadrant. The quadrant data is expected + * to be stored according to the Morton order of + * the quadrants. For quadrant_data->elem_size == 0 + * the function writes an empty field. The section + * header and the padding is still written. + * In this case errcode is set + * to \ref P8EST_FILE_ERR_SUCCESS. + * \param [in] user_string An array of maximal \ref + * P8EST_FILE_USER_STRING_BYTES bytes that + * is written without the NUL-termination + * after the array-dependent metadata and before + * the actual data. If the array is shorter the + * written char array will be padded to the + * right by spaces. The user_string is + * written on rank 0 and therefore also only + * required on rank 0. Can be NULL for other + * ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Return the input context to continue writing + * and eventually closing the file. The return value + * is NULL if the function was called for + * quadrant_data->elem_size == 0. The return + * value is also NULL in case of error but then + * it also holds errcode != 0 and the file is + * tried to close and fc is freed. + */ +p8est_file_context_t *p8est_file_write_field (p8est_file_context_t * fc, + size_t quadrant_size, + sc_array_t * quadrant_data, + const char *user_string, + int *errcode); + +/** Read one (more) per-quadrant data set from a parallel input file. + * This function requires the appropriate number of readable bytes. + * In practice, the data size to read should match the size written. + * This function aborts if the number of bytes to read is bigger than the + * dataset that corresponds to the processor. + * The data size to read is encoded by the element size of quadrant_data + * It is legal to close a file before all data sets have been read. + * + * The function closes and deallocates the file context and returns NULL + * if the bytes the user wants to read exceed the given file and/or + * the element size of the array given by quadrant_data->elem_size does not + * coincide with the element size according to the array metadata given in + * the file. + * + * If the block header information is not matching the passed parameters + * the function sets \ref P8EST_FILE_ERR_FORMAT for errcode. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in,out] fc Context previously created by \ref + * p8est_file_open_read (_ext). It keeps track + * of the data sets read one after another. + * \param [in] quadrant_size The number of bytes per quadrant. This number + * must coincide with \a quadrant_data->elem_size. + * \param [in,out] quadrant_data An array of the length number of local quadrants + * with the element size equal to number of bytes + * read per quadrant. The quadrant data is read + * according to the Morton order of the quadrants. + * \a quadrant_data->elem_size must coincide with + * the section data size in the file. + * quadrant_data == NULL means that the data is + * skipped and the internal file pointer is incremented. + * In the case of skipping \a quadrant_size is still + * checked using the corresponding value read from + * the file. If fc was opened by + * \ref p8est_file_open_read_ext and + * \a fc->global_first_quadrant was not set by the + * user, the function uses a uniform partition to read + * the data field in parallel. + * \a quadrant_data is resized by \ref sc_array_resize. + * \param [in,out] user_string At least \ref P8EST_FILE_USER_STRING_BYTES bytes. + * The user string is read on rank 0 and internally + * broadcasted to all ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program or if + * the function was called with quadrant_data == NULL. + * In case of error the file is tried to close + * and fc is freed. + */ +p8est_file_context_t *p8est_file_read_field (p8est_file_context_t * fc, + size_t quadrant_size, + sc_array_t * quadrant_data, + char *user_string, int *errcode); + +/** A data type that encodes the metadata of one data block in a p4est data file. + */ +typedef struct p8est_file_section_metadata +{ + char block_type; /**< 'H' (header) or 'F' (data file) */ + size_t data_size; /**< data size in bytes per array element ('F') + or of the header section ('H') */ + char user_string[P8EST_FILE_USER_STRING_BYTES]; /**< user string of the data section */ +} +p8est_file_section_metadata_t; + +/** Read metadata information of a file written by a matching forest. + * Matching refers to the global count of quadrants; partition is irrelevant. + * + * This function parses the given file on rank 0 and broadcasts the information + * on the number of data fields contained to all other ranks. Collective call. + * + * This function catches all I/O and file format errors and returns a valid MPI + * error class related to file handling. Errors are collectively synchronized. + * + * If the number of bytes that the user intend to read is larger than the number + * bytes left in the file, the function prints out an information about this + * situation using P8EST_LERROR. In this case the function reads the bytes + * that are possible to read but returns NULL to indicate an error. + * If the file or block header information is not matching the passed parameters + * the function sets \ref P8EST_FILE_ERR_FORMAT for errcode. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in] p8est A p4est that is only required for the + * MPI communicator, and to verify the + * global quadrant count found in the file. + * \param [in] filename Path to parallel file. + * \param [in,out] user_string At least \ref P8EST_FILE_USER_STRING_BYTES + * bytes. This array will be filled with the + * user string of the file after a successful + * call of this function. + * \param [in,out] data_sections After a successful function call this + * variable holds an array with a length + * corresponding to the number of data section + * in the file that are successfully found + * and seeked. The values in the array are the + * number of bytes of stored data per quadrant. + * Require elem_size->elem_size + * == sizeof (p8est_file_block_metadata_t) + * on input and preserve it on output. + * See p8est_file_block_metadata_t to obtain + * detailed information about the data blocks + * of the file. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return 0 for a successful call and -1 in case of + * an error. See also errcode argument. + */ +int p8est_file_info (p8est_t * p8est, const char *filename, + char *user_string, + sc_array_t * data_sections, + int *errcode); + +/** Turn p8est_file errcode into a string. + * + * \param [in] errclass An errcode that is output by a + * p8est_file function. + * \param [in,out] string At least sc_MPI_MAX_ERROR_STRING bytes. + * \param [out] resultlen Length of string on return. + * \return \ref P8EST_FILE_ERR_SUCCESS on success or + * something else on invalid arguments. + */ +int p8est_file_error_string (int errclass, + char *string, int *resultlen); + +/** Write a p8est to an opened file. + * This function does not write the connectvity of the p4est. + * If one want to write the connectivity, \ref p8est_file_write_connectivity + * can be used. + * + * \param [in,out] fc Context previously created by \ref + * p8est_file_open_create. It keeps track + * of the data sets written one after another. + * \param [in] p8est The p8est that is written to the file. + * \param [in] quad_string The string that is used as user string + * for quadrant section. + * An array of maximal \ref + * P8EST_FILE_USER_STRING_BYTES bytes that + * is written without the NUL-termination + * after the section-dependent metadata and before + * the actual data. If the char array is shorter the + * written char array will be padded to the + * right by spaces. The user_string is + * written on rank 0 and therefore also only + * required on rank 0. Can be NULL for other + * ranks. + * \param [in] quad_data_string The string that is used as user string + * for quadrant data section. + * An array of maximal \ref + * P8EST_FILE_USER_STRING_BYTES bytes that + * is written without the NUL-termination + * after the section-dependent metadata and before + * the actual data. If the char array is shorter the + * written char array will be padded to the + * right by spaces. The user_string is + * written on rank 0 and therefore also only + * required on rank 0. Can be NULL for other + * ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program. + * In case of error the file is tried to close + * and fc is freed. + */ +p8est_file_context_t *p8est_file_write_p8est (p8est_file_context_t * fc, + p8est_t * p8est, + const char *quad_string, + const char *quad_data_string, + int *errcode); + +/** Read a p8est to an opened file using the MPI communicator of \a fc. + * + * \param [in,out] fc Context previously created by \ref + * p8est_file_open_read (_ext). It keeps track + * of the data sets read one after another. + * \param [in] conn A connectivity that is used to create + * the \a p8est. + * \param [in] data_size The data size of the p4est that will + * be created by this function. + * \param [out] p8est The p8est that is created from the file. + * \param [in,out] quad_string The user string of the quadrant section. +* At least \ref P8EST_FILE_USER_STRING_BYTES bytes. + * The user string is read on rank 0 and internally + * broadcasted to all ranks. + * \param [in,out] quad_data_string The user string of the quadrant data section. + At least \ref P8EST_FILE_USER_STRING_BYTES bytes. + * The user string is read on rank 0 and internally + * broadcasted to all ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program. + * In case of error the file is tried to close + * and fc is freed. + */ +p8est_file_context_t *p8est_file_read_p8est (p8est_file_context_t * fc, + p8est_connectivity_t * conn, + size_t data_size, + p8est_t ** p8est, + char *quad_string, + char *quad_data_string, + int *errcode); + +/** Write a connectivity to an opened file. + * This function writes two block sections to the opened file. + * The first block contains the size of the serialized connectivity data + * and the second data block contains serialized connectivity. + * + * \param [in,out] fc Context previously created by \ref + * p8est_file_open_create. It keeps track + * of the data sets written one after another. + * \param [in] conn The connectivity that is written to the file. + * \param [in] conn_string The user string that written for the + * connectivity data block section. + * An array of maximal \ref + * P8EST_FILE_USER_STRING_BYTES bytes that + * is written without the NUL-termination + * after the section-dependent metadata and before + * the actual data. If the char array is shorter the + * written char array will be padded to the + * right by spaces. The user_string is + * written on rank 0 and therefore also only + * required on rank 0. Can be NULL for other + * ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program. + * In case of error the file is tried to close + * and fc is freed. + */ +p8est_file_context_t *p8est_file_write_connectivity (p8est_file_context_t * + fc, + p8est_connectivity_t * + conn, + const char *conn_string, + int *errcode); + +/** Read a connectivity from an opened file. + * This function reads two block sections from the opened file. + * The first block contains the size of the serialized connectivity data + * and the second data block contains serialized connectivity. + * + * \param [in,out] fc Context previously created by \ref + * p8est_file_open_read (_ext). It keeps track + * of the data sets written one after another. + * \param [out] conn The connectivity that is read from the file. + * \param [in,out] conn_string The user string that read for the + * connectivity data block section. + * At least \ref P8EST_FILE_USER_STRING_BYTES bytes. + * The user string is read on rank 0 and internally + * broadcasted to all ranks. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return Return a pointer to input context or NULL in case + * of errors that does not abort the program. + * In case of error the file is tried to close + * and fc is freed. + */ +p8est_file_context_t *p8est_file_read_connectivity (p8est_file_context_t * fc, + p8est_connectivity_t ** + conn, char *conn_string, + int *errcode); + +/** Close a file opened for parallel write/read and free the context. + * + * This function does not abort on MPI I/O errors but returns NULL. + * Without MPI I/O the function may abort on file system dependent + * errors. + * + * \param [in,out] fc Context previously created by \ref + * p8est_file_open_create or \ref + * p8est_file_open_read _(ext). Is freed. + * \param [out] errcode An errcode that can be interpreted by \ref + * p8est_file_error_string. + * \return 0 for a successful call and -1 in case of + * an error. See also errcode argument. + */ +int p8est_file_close (p8est_file_context_t * fc, + int *errcode); + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + SC_EXTERN_C_END; #endif /* !P8EST_IO_H */ diff --git a/src/p8est_iterate.h b/src/p8est_iterate.h index 8295ffa86..f8124c015 100644 --- a/src/p8est_iterate.h +++ b/src/p8est_iterate.h @@ -32,7 +32,6 @@ #ifndef P8EST_ITERATE_H #define P8EST_ITERATE_H -#include #include SC_EXTERN_C_BEGIN; @@ -240,7 +239,7 @@ typedef struct p8est_iter_corner_side } p8est_iter_corner_side_t; -/** The information that is availalbe to the user-defined p8est_iter_corner_t +/** The information that is available to the user-defined p8est_iter_corner_t * callback. * * If tree_boundary is false, the corner is on the interior of a tree. diff --git a/src/p8est_lnodes.h b/src/p8est_lnodes.h index 8208dd413..be060ed9c 100644 --- a/src/p8est_lnodes.h +++ b/src/p8est_lnodes.h @@ -25,7 +25,6 @@ #ifndef P8EST_LNODES_H #define P8EST_LNODES_H -#include #include SC_EXTERN_C_BEGIN; diff --git a/src/p8est_mesh.h b/src/p8est_mesh.h index a087be352..09af56b9f 100644 --- a/src/p8est_mesh.h +++ b/src/p8est_mesh.h @@ -24,7 +24,19 @@ /** \file p8est_mesh.h * - * forest topology in a conventional mesh format + * Forest topology in a conventional mesh format. + * + * A typical workflow starts with \ref p8est_mesh_params_init to initialize a + * \ref p8est_mesh_params_t, followed by eventual user-dependent changes to the + * parameters. + * + * Next a \ref p8est_mesh_t is created with \ref p8est_mesh_new_params. + * + * Now, the user can create a \ref p8est_mesh_face_neighbor_t with + * \ref p8est_mesh_face_neighbor_init and loop over a quadrants face neighbors + * by repeated calls to \ref p8est_mesh_face_neighbor_next. + * + * Once done, the mesh has to be destroyed with \ref p8est_mesh_destroy. * * \ingroup p8est */ @@ -36,17 +48,37 @@ SC_EXTERN_C_BEGIN; +/** This structure contains the different parameters of mesh creation. + * A default instance can be initialzed by calling \ref p8est_mesh_params_init + * and used for mesh creation by calling \ref p8est_mesh_new_params. */ +typedef struct +{ + int compute_tree_index; /**< Boolean to decide whether to + allocate and compute the + quad_to_tree list. */ + int compute_level_lists; /**< Boolean to decide whether to + compute the level lists in + quad_level. */ + p8est_connect_type_t btype; /**< Flag indicating the + connection types (face, edge, + corner) stored in the mesh. */ + int edgehanging_corners; /**< Boolean to decide whether to + add corner neighbors across + coarse edges. */ +} +p8est_mesh_params_t; + /** This structure contains complete mesh information on a 2:1 balanced forest. * It stores the locally relevant neighborhood, that is, all locally owned * quadrants and one layer of adjacent ghost quadrants and their owners. * * For each local quadrant, its tree number is stored in quad_to_tree. * The quad_to_tree array is NULL by default and can be enabled using - * \ref p8est_mesh_new_ext. + * \ref p8est_mesh_new_ext or \ref p8est_mesh_new_params. * For each ghost quadrant, its owner rank is stored in ghost_to_proc. * For each level, an array of local quadrant numbers is stored in quad_level. * The quad_level array is NULL by default and can be enabled using - * \ref p8est_mesh_new_ext. + * \ref p8est_mesh_new_ext or \ref p8est_mesh_new_params. * * The quad_to_quad list stores one value for each local quadrant's face. * This value is in 0..local_num_quadrants-1 for local quadrants, or in @@ -125,20 +157,23 @@ SC_EXTERN_C_BEGIN; * * Corners with no diagonal neighbor at all are assigned the value -3. This * only happens on the domain boundary, which is necessarily a tree boundary. - * Corner-neighbors for face- and edge-hanging nodes are assigned the value -1. + * If edgehanging_corners in params is 0, all corner-neighbors for face- and + * edge-hanging nodes are assigned the value -1. + * If it is 1, we check for corner neighbors across coarse edges and assign -1 + * for the remaining face- and edge-hanging nodes. * - * TODO: In case of an inter-tree neighbor relation in a brick-like - * situation (one same-size neighbor, diagonally opposite edge/corner), - * use the same encoding as for edges/corners within a tree. + * The params struct describes the parameters the mesh was created with. + * For full control over the parameters, use \ref p8est_mesh_new_params for + * mesh creation. */ typedef struct { - p4est_locidx_t local_num_quadrants; - p4est_locidx_t ghost_num_quadrants; + p4est_locidx_t local_num_quadrants; /**< number of process-local quadrants */ + p4est_locidx_t ghost_num_quadrants; /**< number of ghost-layer quadrants */ p4est_topidx_t *quad_to_tree; /**< tree index for each local quad. - Is NULL by default, but may be - enabled by \ref p8est_mesh_new_ext. */ + Is NULL if compute_tree_index in + params is 0. */ int *ghost_to_proc; /**< processor for each ghost quad */ p4est_locidx_t *quad_to_quad; /**< one index for each of the 6 faces */ @@ -148,22 +183,29 @@ typedef struct The array has entries indexed by 0..P4EST_QMAXLEVEL inclusive that are arrays of local quadrant ids. - Is NULL by default, but may be - enabled by \ref p8est_mesh_new_ext. */ + Is NULL if compute_level_lists in + params is 0. */ - /* These members are NULL if edges are not requested in \ref p8est_mesh_new. */ + /* These members are NULL if btype in params is < P8EST_CONNECT_EDGE and can + * be requested in \ref p8est_mesh_new. */ p4est_locidx_t local_num_edges; /**< unsame-size and tree-boundary edges */ p4est_locidx_t *quad_to_edge; /**< 12 indices for each local quad */ sc_array_t *edge_offset; /**< local_num_edges + 1 entries */ sc_array_t *edge_quad; /**< edge_offset indexes into this */ sc_array_t *edge_edge; /**< and this one too (type int8_t) */ - /* These members are NULL if corners are not requested in \ref p8est_mesh_new. */ - p4est_locidx_t local_num_corners; /* tree-boundary corners */ - p4est_locidx_t *quad_to_corner; /* 8 indices for each local quad */ - sc_array_t *corner_offset; /* local_num_corners + 1 entries */ - sc_array_t *corner_quad; /* corner_offset indexes into this */ - sc_array_t *corner_corner; /* and this one too (type int8_t) */ + /* These members are NULL if btype in params is < P8EST_CONNECT_CORNER and can + * be requested in \ref p8est_mesh_new. */ + p4est_locidx_t local_num_corners; /**< tree-boundary corners */ + p4est_locidx_t *quad_to_corner; /**< 8 indices for each local quad */ + sc_array_t *corner_offset; /**< local_num_corners + 1 entries */ + sc_array_t *corner_quad; /**< corner_offset indexes into this */ + sc_array_t *corner_corner; /**< and this one too (type int8_t) */ + + p8est_mesh_params_t params; /**< parameters the mesh was created + with, e.g. by passing them to + \ref p8est_mesh_new_ext or + \ref p8est_mesh_new_params */ } p8est_mesh_t; @@ -173,21 +215,22 @@ p8est_mesh_t; typedef struct { /* forest information */ - p8est_t *p4est; - p8est_ghost_t *ghost; - p8est_mesh_t *mesh; + p8est_t *p4est; /**< the forest */ + p8est_ghost_t *ghost; /**< the ghost layer of the forest */ + p8est_mesh_t *mesh; /**< a mesh derived from the forest */ /* quadrant information */ - p4est_topidx_t which_tree; - p4est_locidx_t quadrant_id; /* tree-local quadrant index */ - p4est_locidx_t quadrant_code; /* 6 * (quadrant_id + tree_offset) */ + p4est_topidx_t which_tree; /**< the current tree index */ + p4est_locidx_t quadrant_id; /**< tree-local quadrant index */ + p4est_locidx_t quadrant_code; /**< 6 * (quadrant_id + tree_offset) */ /* neighbor information */ - int face; /* Face number in 0..5. */ - int subface; /* Hanging neighbor number in 0..3. */ + int face; /**< Face number in 0..5. */ + int subface; /**< Hanging neighbor number in 0..3. */ /* internal information */ - p4est_locidx_t current_qtq; + p4est_locidx_t current_qtq; /**< track index of current neighboring + quadrant */ } p8est_mesh_face_neighbor_t; @@ -197,9 +240,15 @@ p8est_mesh_face_neighbor_t; */ size_t p8est_mesh_memory_used (p8est_mesh_t * mesh); +/** Initialize a default p8est_mesh_params_t structure. + * The parameters are set to create the most basic mesh structure, without + * tree index and level lists and considering only face connections. */ +void p8est_mesh_params_init (p8est_mesh_params_t * params); + /** Create a p8est_mesh structure. - * This function does not populate the quad_to_tree and quad_level fields. - * To populate them, use \ref p8est_mesh_new_ext. + * This function does not populate the quad_to_tree and quad_level fields and + * ignores corner neighbors across edge-hanging corners. + * To populate them, use \ref p8est_mesh_new_params. * \param [in] p8est A forest that is fully 2:1 balanced. * \param [in] ghost The ghost layer created from the provided p4est. * \param [in] btype Determines the highest codimension of neighbors. @@ -208,6 +257,17 @@ size_t p8est_mesh_memory_used (p8est_mesh_t * mesh); p8est_mesh_t *p8est_mesh_new (p8est_t * p8est, p8est_ghost_t * ghost, p8est_connect_type_t btype); +/** Create a new mesh. + * \param [in] p8est A forest that is fully 2:1 balanced. + * \param [in] ghost The ghost layer created from the provided p4est. + * \param [in] params The mesh creation parameters. If NULL, the function + * defaults to the parameters of \ref p8est_mesh_params_init. + * \return A fully allocated mesh structure. + */ +p8est_mesh_t *p8est_mesh_new_params (p8est_t * p8est, + p8est_ghost_t * ghost, + p8est_mesh_params_t * params); + /** Destroy a p8est_mesh structure. * \param [in] mesh Mesh structure previously created by p8est_mesh_new. */ @@ -226,6 +286,7 @@ p8est_quadrant_t *p8est_mesh_get_quadrant (p8est_t * p4est, p8est_mesh_t * mesh, p4est_locidx_t qid); +/*** OUTDATED FUNCTION ***/ /** Lookup neighboring quads of quadrant in a specific direction * \param [in] p4est Forest to be worked with. * \param [in] ghost Ghost quadrants. @@ -238,7 +299,7 @@ p8est_quadrant_t *p8est_mesh_get_quadrant (p8est_t * p4est, * 18 .. 25 neighbor(-s) across c_{i-18} * \param [out] neighboring_quads Array containing neighboring quad(-s) * Needs to be empty, contains - * p4est_quadrant_t*. May be NULL, then \ref + * p4est_quadrant_t*. May be NULL, then \b * neighboring_qids must not be NULL. * \param [out] neighboring_encs Array containing encodings for neighboring * quads as described below @@ -304,6 +365,9 @@ p8est_quadrant_t *p8est_mesh_quadrant_cumulative (p8est_t * p8est, /** Initialize a mesh neighbor iterator by quadrant index. * \param [out] mfn A p8est_mesh_face_neighbor_t to be initialized. + * \param [in] p8est Forest to be worked with. + * \param [in] ghost Ghost layer of the forest. + * \param [in] mesh A mesh derived from the forest. * \param [in] which_tree Tree of quadrant whose neighbors are looped over. * \param [in] quadrant_id Index relative to which_tree of quadrant. */ @@ -317,6 +381,9 @@ void p8est_mesh_face_neighbor_init2 (p8est_mesh_face_neighbor_t /** Initialize a mesh neighbor iterator by quadrant pointer. * \param [out] mfn A p8est_mesh_face_neighbor_t to be initialized. + * \param [in] p8est Forest to be worked with. + * \param [in] ghost Ghost layer of the forest. + * \param [in] mesh A mesh derived from the forest. * \param [in] which_tree Tree of quadrant whose neighbors are looped over. * \param [in] quadrant Pointer to quadrant contained in which_tree. */ @@ -333,7 +400,8 @@ void p8est_mesh_face_neighbor_init (p8est_mesh_face_neighbor_t * \param [out] ntree If not NULL, the tree number of the neighbor. * \param [out] nquad If not NULL, the quadrant number within tree. * For ghosts instead the number in ghost layer. - * \param [out] nface If not NULL, neighbor's face as in p8est_mesh_t. + * \param [out] nface If not NULL, neighbor's face encoding as in + quad_to_face array of p8est_mesh_t. * \param [out] nrank If not NULL, the owner process of the neighbor. * \return Either a real quadrant or one from the ghost layer. * Returns NULL when the iterator is done. diff --git a/src/p8est_nodes.h b/src/p8est_nodes.h index 1a1c923ed..12671bfaa 100644 --- a/src/p8est_nodes.h +++ b/src/p8est_nodes.h @@ -25,7 +25,6 @@ #ifndef P8EST_NODES_H #define P8EST_NODES_H -#include #include SC_EXTERN_C_BEGIN; diff --git a/src/p8est_points.h b/src/p8est_points.h index 42a39e752..dcf9da064 100644 --- a/src/p8est_points.h +++ b/src/p8est_points.h @@ -22,14 +22,6 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/******************************************************************** - * IMPORTANT NOTE * - * * - * The p4est_points functionality depends on sc/src/sc_sort. * - * That parallel bitonic sort is still buggy (see sc/bugs). * - * If you want to use this code you have to fix the sort first. * - ********************************************************************/ - #ifndef P8EST_POINTS_H #define P8EST_POINTS_H diff --git a/src/p8est_search.h b/src/p8est_search.h index c513c4002..0d32b1009 100644 --- a/src/p8est_search.h +++ b/src/p8est_search.h @@ -72,13 +72,19 @@ SC_EXTERN_C_BEGIN; * value of \a search_in \a end is set to \a num_procs - 1. * If none of the above conditions is satisfied, the output is not well defined. * We require `my_begin <= my_begin'. - * \param [in] num_procs Number of processes to get the length of \a search_in. - * \param [in] search_in The sorted array (ascending) in that the function will search. - * If `k` indexes search_in, then `0 <= k < num_procs`. - * \param [in] my_begin The first target that defines the start of the search window. - * \param [in] my_end The second target that defines the end of the search window. - * \param [in,out] begin The first offset such that `search_in[begin] >= my_begin`. - * \param [in,out] end The second offset such that `my_end <= search_in[end]`. + * \param [in] num_procs Number of processes to get the length of + * \a search_in. + * \param [in] search_in The sorted array (ascending) in that the function + * will search. If `k` indexes search_in, then `0 <= + * k < num_procs`. + * \param [in] my_begin The first target that defines the start of the + * search window. + * \param [in] my_end The second target that defines the end of the + * search window. + * \param [in,out] begin The first offset such that + * `search_in[begin] >= my_begin`. + * \param [in,out] end The second offset such that + * `my_end <= search_in[end]`. */ void p8est_find_partition (const int num_procs, p4est_gloidx_t * search_in, @@ -109,7 +115,7 @@ ssize_t p8est_find_higher_bound (sc_array_t * array, * which means that it is advisable NOT to use this function if possible, * and to try to maintain O(1) tree context information in the calling code. * - * \param [in] p8est Forest to be worked with. + * \param [in] p8est Forest to work with. * \param [in] cumulative_id Cumulative index over all trees of quadrant. * \param [in,out] which_tree If not NULL, the input value can be -1 * or an initial guess for the quadrant's tree. @@ -250,7 +256,8 @@ typedef p8est_search_local_t p8est_search_query_t; * empty array, the recursion will stop immediately! * * \param [in] p4est The forest to be searched. - * \param [in] call_post If true, call quadrant callback both pre and post. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). * \param [in] quadrant_fn Executed once when a quadrant is entered, and once * when it is left (the second time only if points are * present and the first call returned true). @@ -282,6 +289,81 @@ void p8est_search (p8est_t * p4est, p8est_search_query_t point_fn, sc_array_t * points); +/** Callback function to query, reorder, and reduce a set of quadrants. + * It receives an array of quadrants and an array of array indices on input. + * On output, the array of quadrants is unmodified but the indices may be. + * This function may permute the indices and/or choose a subset. + * Subsetting is effected by resizing the index array. Note to resize only + * before or after, but not during eventual sorting, since resizing may + * reallocate and thus move the array memory. + * Indices must remain a permutation. + * \param [in] p4est The forest to be queried. + * \param [in] quadrants The quadrant array under consideration, + * each with valid coordinates and level. + * The user data piggy1 field of each quadrant + * contains its tree number. Sorted ascending. + * \param [in,out] indices This array holds \ref p4est_topidx_t types. + * Sorted ascending on input. May be permuted and + * subset by this function. It is explicitly allowed + * to \ref sc_array_resize to smaller length. + * An output length of zero stops recursion. + * \return Return false to break the search recursion. + */ +typedef int (*p8est_search_reorder_t) (p8est_t * p4est, + sc_array_t * quadrants, + sc_array_t * indices); + +/** Run a depth-first traversal, optionally filtering search points. + * There are three main differences to \ref p8est_search_local : + * + * * Before beginning the recursion, we call the \a reorder_fn callback + * with an index array enumerating the local tree roots. The callback + * may permute its entries to define the order of trees to traverse. + * * After the pre-quadrant callback and its point callbacks, the \a + * reorder callback is passed an index array to relevant child numbers + * of the branch quadrant, ordered but possibly non-contiguous. It may + * permute these to indicate the sequence of the children to traverse. + * * The post-quadrant callback is executed after the recursion returns. + * Even for leaves, it is called whenever the pre-callback returned true. + * Even called when all points have been unmatched by the point callback. + * + * \param [in] p4est The forest to be searched. + * \param [in] skip_levels If true and there is a search window that + * contains a single descendant, or if all quadrants + * in the search window are descendants of one child + * of it, skip the intermediate recursion levels. + * \param [in] reorder_fn Called with \a quadrants input array containing + * either the local tree roots or a set of siblings. + * The array may be permuted on output to define the + * order of traversal of the quadrants. + * May be NULL to omit reordering, always recurse. + * If not NULL and it returns true, don't recurse. + * \param [in] pre_quadrant_fn As in \ref p8est_search_local, pre-order callback. + * If the pre-callback returns false, recursion stops. + * If it returns true, recursion continues. + * The \a quadrant argument is the same pre and post. + * \param [in] post_quadrant_fn As in \ref p8est_search_local, post-order callback. + * It is called whenever the pre-callback returns true. + * The \a quadrant argument is the same pre and post. + * \param [in] point_fn As in \ref p8est_search_local. + * \param [in,out] points As in \ref p8est_search_local. + * May be NULL to use quadrant callbacks only. + * Otherwise, if no points remain for a + * particular search quadrant, the recursion + * stops even if the quadrant callback indicates + * to continue. This behavior can be prevented + * by always keeping one bogus point around. + */ +void p8est_search_reorder (p8est_t * p4est, + int skip_levels, + p8est_search_reorder_t reorder_fn, + p8est_search_local_t + pre_quadrant_fn, + p8est_search_local_t + post_quadrant_fn, + p8est_search_local_t point_fn, + sc_array_t * points); + /** Callback function for the partition recursion. * \param [in] p4est The forest to traverse. * Its local quadrants are never accessed. @@ -312,12 +394,13 @@ typedef int (*p8est_search_partition_t) (p8est_t * p4est, * go down branches that are split between multiple processors. The callback * functions can be used to stop a branch recursion even for split branches. * This function offers the option to search for arbitrary user-defined points - * analogously to \ref p4est_search_local. + * analogously to \ref p8est_search_local. * \note Traversing the whole processor partition will be at least O(P), * so sensible use of the callback function is advised to cut it short. * \param [in] p4est The forest to traverse. * Its local quadrants are never accessed. - * \param [in] call_post If true, call quadrant callback both pre and post. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). * \param [in] quadrant_fn This function controls the recursion, * which only continues deeper if this * callback returns true for a branch quadrant. @@ -329,11 +412,84 @@ typedef int (*p8est_search_partition_t) (p8est_t * p4est, * passed to the callback \b point_fn. * See \ref p8est_search_local for details. */ -void p8est_search_partition (p8est_t * p4est, int call_post, +void p8est_search_partition (p8est_t *p4est, int call_post, p8est_search_partition_t quadrant_fn, p8est_search_partition_t point_fn, - sc_array_t * points); + sc_array_t *points); + +/** Traverse some given global partition top-down. + * The partition can be that of any p8est, not necessarily known to the + * caller. This is not a collective function. It does not communicate. + * We proceed top-down through the partition, identically on all processors + * except for the results of two user-provided callbacks. The recursion will only + * go down branches that are split between multiple processors. The callback + * functions can be used to stop a branch recursion even for split branches. + * This function offers the option to search for arbitrary user-defined points + * analogously to \ref p8est_search_local. + * \note Traversing the whole given partition will be at least O(P), + * so sensible use of the callback function is advised to cut it short. + * \param [in] gfq Partition offsets to traverse. Length \a nmemb + 1. + * \param [in] gfp Partition position to traverse. Length \a nmemb + 1. + * \param [in] nmemb Number of processors encoded in \a gfq (plus one). + * \param [in] num_trees Tree number must match the contents of \a gfq. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). + * \param [in] user We pass a dummy p8est to the callbacks whose only + * valid element is its user_pointer set to \a user. + * \param [in] quadrant_fn This function controls the recursion, + * which only continues deeper if this + * callback returns true for a branch quadrant. + * It is allowed to set this to NULL. + * \param [in] point_fn This function decides per-point whether it is + * followed down the recursion. + * Must be non-NULL if \b points are not NULL. + * \param [in] points User-provided array of \b points that are + * passed to the callback \b point_fn. + * See \ref p8est_search_local for details. + */ +void p8est_search_partition_gfx + (const p4est_gloidx_t *gfq, const p8est_quadrant_t *gfp, + int nmemb, p4est_topidx_t num_trees, int call_post, void *user, + p8est_search_partition_t quadrant_fn, p8est_search_partition_t point_fn, + sc_array_t *points); + +/** Traverse some given global partition top-down. + * The partition can be that of any p4est, not necessarily known to the + * caller. This is not a collective function. It does not communicate. + * We proceed top-down through the partition, identically on all processors + * except for the results of two user-provided callbacks. The recursion will only + * go down branches that are split between multiple processors. The callback + * functions can be used to stop a branch recursion even for split branches. + * This function offers the option to search for arbitrary user-defined points + * analogously to \ref p8est_search_local. + * This function is similar to \ref p8est_search_partition_gfx, but does not + * require the \ref p4est_gloidx_t array gfq. If gfq is available, using + * \ref p8est_search_partition_gfx is recommended, because it is slightly faster. + * \note Traversing the whole given partition will be at least O(P), + * so sensible use of the callback function is advised to cut it short. + * \param [in] gfp Partition position to traverse. Length \a nmemb + 1. + * \param [in] nmemb Number of processors encoded in \a gfp (plus one). + * \param [in] num_trees Tree number must match the contents of \a gfp. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). + * \param [in] user We pass a dummy p4est to the callbacks whose only + * valid element is its user_pointer set to \a user. + * \param [in] quadrant_fn This function controls the recursion, + * which only continues deeper if this + * callback returns true for a branch quadrant. + * It is allowed to set this to NULL. + * \param [in] point_fn This function decides per-point whether it is + * followed down the recursion. + * Must be non-NULL if \b points are not NULL. + * \param [in] points User-provided array of \b points that are + * passed to the callback \b point_fn. + * See \ref p8est_search_local for details. + */ +void p8est_search_partition_gfp + (const p8est_quadrant_t *gfp, int nmemb, p4est_topidx_t num_trees, + int call_post, void *user, p8est_search_partition_t quadrant_fn, + p8est_search_partition_t point_fn, sc_array_t *points); /** Callback function for the top-down search through the whole forest. * \param [in] p4est The forest to search. @@ -393,8 +549,8 @@ typedef int (*p8est_search_all_t) (p8est_t * p8est, /** Perform a top-down search on the whole forest. * - * This function combines the functionality of \ref p4est_search_local and \ref - * p4est_search_partition; their documentation applies for the most part. + * This function combines the functionality of \ref p8est_search_local and \ref + * p8est_search_partition; their documentation applies for the most part. * * The recursion proceeds from the root quadrant of each tree until * (a) we encounter a remote quadrant that covers only one processor, or @@ -425,11 +581,12 @@ typedef int (*p8est_search_all_t) (p8est_t * p8est, * \note * This function works fine when used for the special cases that either the * partition or the local quadrants are not of interest. However, in the case - * of querying only local information we expect that \ref p4est_search_local + * of querying only local information we expect that \ref p8est_search_local * will be faster since it employs specific local optimizations. * * \param [in] p4est The forest to be searched. - * \param [in] call_post If true, call quadrant callback both pre and post. + * \param [in] call_post If true, call quadrant callback both pre and post + * point callback, in both cases before recursion (!). * \param [in] quadrant_fn Executed once for each quadrant that is entered. * If the callback returns false, this quadrant and * its descendants are excluded from the search, and diff --git a/src/p8est_vtk.h b/src/p8est_vtk.h index 27283623d..1606540a9 100644 --- a/src/p8est_vtk.h +++ b/src/p8est_vtk.h @@ -50,8 +50,8 @@ typedef struct p8est_vtk_context p8est_vtk_context_t; * This function will abort if there is a file error. * * \param [in] p8est The p8est to be written. - * \param [in] geom A p8est_geometry_t structure or NULL for vertex space - * as defined by p8est->connectivity. + * \param [in] geom A \ref p8est_geometry_t structure or NULL for vertex space + * as defined by the \a p8est's \ref p8est_connectivity_t member. * \param [in] filename The first part of the file name which will have the * MPI rank appended to it: The output file will be * filename_rank.vtu, and the meta file filename.pvtu. @@ -163,6 +163,30 @@ void p8est_vtk_context_destroy (p8est_vtk_context_t * context); */ p8est_vtk_context_t *p8est_vtk_write_header (p8est_vtk_context_t * cont); +/** Write the VTK header for higher order visualization. + * + * This function follows the same routines as p8est_vtk_write_header. + * In addition, the caller must pass in an array containing coordinates for + * each point, as well as an integer representing the number of points in + * one direction each element has (for example, in an 8x8x8 cell, pass in 8). + * + * \param [in,out] cont A VTK context created by \ref p8est_vtk_context_new. + * None of the vtk_write functions must have been called. + * This context is the return value if no error occurs. + * \param [in] positions An sc_array_t of doubles containing the coordinates + * of all points to be written. Ordering of data is + * [ x_0, y_0, (z_0) ... x_n, y_n, (z_n) ] + * \param [in] Nnodes1D Integer number of points in each element in 1D. + * + * \return On success, an opaque context (p8est_vtk_context_t) pointer + * that must be passed to subsequent p8est_vtk calls. It is + * required to call \ref p8est_vtk_write_footer eventually with + * this value. Returns NULL on error. + */ +p8est_vtk_context_t *p8est_vtk_write_header_ho (p8est_vtk_context_t * cont, + sc_array_t * positions, + int Nnodes1D); + /** Write VTK cell data. * * There are options to have this function write diff --git a/src/p8est_wrap.h b/src/p8est_wrap.h index fd0e05c4f..ae4ac5f2a 100644 --- a/src/p8est_wrap.h +++ b/src/p8est_wrap.h @@ -32,7 +32,6 @@ * quadrants, respectively, which can help make application code cleaner. */ -#include #include #include @@ -48,22 +47,44 @@ typedef enum p8est_wrap_flags } p8est_wrap_flags_t; -typedef struct p8est_wrap +/** This structure contains the different parameters of wrap creation. + * A default instance can be initialized by calling \ref p8est_wrap_params_init + * and used for wrap creation by calling \ref p8est_wrap_new_params. */ +typedef struct { - /* this member is never used or changed by p8est_wrap */ - void *user_pointer; /**< Convenience member for users */ - - /** If true, this wrap has NULL for ghost, mesh, and flag members. - * If false, they are properly allocated and kept current internally. */ - int hollow; - - /** Non-negative integer tells us how many adaptations to wait - * before any given quadrant may be coarsened again. */ - int coarsen_delay; + int hollow; /**< Do not allocate flags, ghost, and + mesh members. */ + p8est_mesh_params_t mesh_params; /**< Parameters for mesh creation. The + btype member is used for ghost + creation as well. */ + p8est_replace_t replace_fn; /**< This member may be removed soon. + Callback to replace quadrants during + refinement, coarsening or balancing + in \ref p8est_wrap_adapt. May be NULL. + The callback should not change the + p8est's user data. */ + int coarsen_delay; /**< Non-negative integer telling how + many adaptations to wait before any + given quadrant may be coarsened + again. */ + int coarsen_affect; /**< Boolean: If true, we delay + coarsening not only after refinement, + but also between subsequent + coarsenings of the same quadrant. */ + int partition_for_coarsening; /**< If true, the partition is + modified to allow one level + of coarsening when calling + \ref p8est_wrap_partition. */ + void *user_pointer; /**< Set the user pointer in + \ref p8est_wrap_t. Subsequently, we + will never access it. */ +} +p8est_wrap_params_t; - /** Boolean: If true, we delay coarsening not only after refinement, - * but also between subsequent coarsenings of the same quadrant. */ - int coarsen_affect; +typedef struct p8est_wrap +{ + /* collection of wrap-related parameters */ + p8est_wrap_params_t params; /** This reference counter is a workaround for internal use only. * Until we have refcounting/copy-on-write for the connectivity, @@ -79,8 +100,6 @@ typedef struct p8est_wrap int p4est_half; int p4est_faces; int p4est_children; - p8est_connect_type_t btype; - p8est_replace_t replace_fn; p8est_t *p4est; /**< p4est->user_pointer is used internally */ /* anything below here is considered private und should not be touched */ @@ -97,9 +116,15 @@ typedef struct p8est_wrap } p8est_wrap_t; +/** Initialize a default \ref p8est_wrap_params_t structure. + * The parameters are set to create the most basic, hollow wrap structure. */ +void p8est_wrap_params_init (p8est_wrap_params_t * params); + /** Create a p8est wrapper from a given connectivity structure. * The ghost and mesh members are initialized as well as the flags. * The btype is set to P8EST_CONNECT_FULL. + * This function sets a subset of the wrap creation parameters. For full control + * use \ref p8est_wrap_new_params. * \param [in] mpicomm We expect sc_MPI_Init to be called already. * \param [in] conn Connectivity structure. Wrap takes ownership. * \param [in] initial_level Initial level of uniform refinement. @@ -113,6 +138,8 @@ p8est_wrap_t *p8est_wrap_new_conn (sc_MPI_Comm mpicomm, * \param [in,out] p8est Valid p8est object that we will own. * We take ownership of its connectivity too. * Its user pointer must be NULL and will be changed. + * Its data size will be set to 0 and the quadrant + * data will be freed. * \param [in] hollow Do not allocate flags, ghost, and mesh members. * \param [in] btype The neighborhood used for balance, ghost, mesh. * \param [in] replace_fn Callback to replace quadrants during refinement, @@ -127,8 +154,26 @@ p8est_wrap_t *p8est_wrap_new_p8est (p8est_t * p8est, int hollow, p8est_replace_t replace_fn, void *user_pointer); +/** Create a wrapper for a given p8est structure. + * Like \ref p8est_wrap_new_p8est, but with \a params to completely control the + * wrap creation process. + * \param [in,out] p8est Valid p8est object that we will own. + * We take ownership of its connectivity too. + * Its user pointer must be NULL and will be changed. + * Its data size will be set to 0 and the quadrant + * data will be freed. + * \param [in] params The wrap creation parameters. If NULL, the function + * defaults to the parameters of + * \ref p8est_wrap_params_init. + * \return A fully initialized p4est_wrap structure. + */ +p8est_wrap_t *p8est_wrap_new_p8est_params (p8est_t * p8est, + p8est_wrap_params_t * params); + /** Create a p8est wrapper from a given connectivity structure. - * Like p8est_wrap_new_conn, but with extra parameters \a hollow and \a btype. + * Like \ref p8est_wrap_new_conn, but with extra parameters \a hollow and \a btype. + * This function sets a subset of the wrap creation parameters. For full control + * use \ref p8est_wrap_new_params. * \param [in] mpicomm We expect sc_MPI_Init to be called already. * \param [in] conn Connectivity structure. Wrap takes ownership. * \param [in] initial_level Initial level of uniform refinement. @@ -149,6 +194,23 @@ p8est_wrap_t *p8est_wrap_new_ext (sc_MPI_Comm mpicomm, p8est_replace_t replace_fn, void *user_pointer); +/** Create a p8est wrapper from a given connectivity structure. + * Like \ref p8est_wrap_new_conn, but with \a params to completely control the + * wrap creation process. + * \param [in] mpicomm We expect sc_MPI_Init to be called already. + * \param [in] conn Connectivity structure. Wrap takes ownership. + * \param [in] initial_level Initial level of uniform refinement. + * No effect if less/equal to zero. + * \param [in] params The wrap creation parameters. If NULL, the function + * defaults to the parameters of + * \ref p8est_wrap_params_init. + * \return A fully initialized p8est_wrap structure. + */ +p8est_wrap_t *p8est_wrap_new_params (sc_MPI_Comm mpicomm, + p8est_connectivity_t * conn, + int initial_level, + p8est_wrap_params_t * params); + /** Create a p8est wrapper from an existing one. * \note This wrapper must be destroyed before the original one. * We set it to hollow and copy the original p8est data structure. @@ -208,6 +270,21 @@ void p8est_wrap_set_coarsen_delay (p8est_wrap_t * pp, int coarsen_delay, int coarsen_affect); +/** Set a parameter that ensures future partitions allow one level of coarsening. + * The partition_for_coarsening parameter is passed to \ref p8est_partition_ext + * in \ref p8est_wrap_partition. + * If not zero, all future calls to \ref p8est_wrap_partition will partition + * in a manner that allows one level of coarsening. This function does not + * automatically repartition the mesh, when switching partition_for_coarsening + * to a non-zero value. + * \param [in,out] pp A valid p8est_wrap structure. + * \param [in] partition_for_coarsening Boolean: If true, all future partitions + * of the wrap allow one level of coarsening. + * Suggested default: 1. + */ +void p8est_wrap_set_partitioning (p8est_wrap_t *pp, + int partition_for_coarsening); + /** Return the appropriate ghost layer. * This function is necessary since two versions may exist simultaneously * after refinement and before partition/complete. @@ -246,6 +323,8 @@ void p8est_wrap_mark_coarsen (p8est_wrap_t * pp, * Checks pp->flags as per-quadrant input against p8est_wrap_flags_t. * The pp->flags array is updated along with p8est and reset to zeros. * Creates ghost_aux and mesh_aux to represent the intermediate mesh. + * If zlib is available, the routine checks whether coarsening and balancing the + * p8est canceled out and skips computing ghost_aux and mesh_aux when possible. * \param [in,out] pp The p8est wrapper to work with, must not be hollow. * \return boolean whether p8est has changed. * If true, partition must be called. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 86065137e..88bf6a452 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,80 +1,97 @@ +include(CTest) + if(P4EST_HAVE_STDLIB_H) check_symbol_exists(random stdlib.h P4EST_HAVE_RANDOM) check_symbol_exists(srandom stdlib.h P4EST_HAVE_SRANDOM) endif() -set(test test_comm test_hash test_order test_complete_subtree) +set(p4est_tests test_comm test_hash test_order test_complete_subtree +test_conn_transformation2 test_brick2 test_join2 test_conn_reduce2 +) -list(APPEND tests test_conn_transformation2 test_brick2 test_join2 test_conn_reduce2) +list(APPEND tests test_conn_transformation2 test_brick2 test_join2 test_conn_reduce2 test_version) if(P4EST_HAVE_ARPA_INET_H OR P4EST_HAVE_NETINET_IN_H OR P4EST_HAVE_WINSOCK2_H) # htonl - list(APPEND tests test_balance2 test_partition_corr2 test_coarsen2 test_balance_type2 test_lnodes2 test_plex2 test_connrefine2 test_search2 test_subcomm2 test_replace2 test_ghost2 test_iterate2 test_nodes2 test_partition2 test_quadrants2 test_valid2 test_conn_complete2 test_wrap2) + list(APPEND p4est_tests test_balance2 test_partition_corr2 test_coarsen2 test_balance_type2 test_lnodes2 test_plex2 test_connrefine2 test_search2 test_subcomm2 test_replace2 test_ghost2 test_iterate2 test_nodes2 test_partition2 test_quadrants2 test_valid2 test_conn_complete2 test_wrap2) if(P4EST_HAVE_GETOPT_H) - list(APPEND tests test_load2 test_loadsave2) + list(APPEND p4est_tests test_load2 test_loadsave2) endif() - endif() if(P4EST_HAVE_RANDOM AND P4EST_HAVE_SRANDOM) - list(APPEND tests test_balance_seeds2) + list(APPEND p4est_tests test_balance_seeds2) endif() if(enable_p8est) - list(APPEND tests test_conn_transformation3 test_brick3 test_join3 test_conn_reduce3) + set(p8est_tests test_conn_transformation3 test_brick3 test_join3 test_conn_reduce3 test_mesh_corners3) if(P4EST_HAVE_ARPA_INET_H OR P4EST_HAVE_NETINET_IN_H OR P4EST_HAVE_WINSOCK2_H) # htonl - list(APPEND tests test_balance3 test_partition_corr3 test_coarsen3 test_balance_type3 test_lnodes3 test_plex3 test_connrefine3 test_subcomm3 test_replace3 test_ghost3 test_iterate3 test_nodes3 test_partition3 test_quadrants3 test_valid3 test_conn_complete3 test_wrap3) + list(APPEND p8est_tests test_balance3 test_partition_corr3 test_coarsen3 test_balance_type3 test_lnodes3 test_plex3 test_connrefine3 test_subcomm3 test_replace3 test_ghost3 test_iterate3 test_nodes3 test_partition3 test_quadrants3 test_valid3 test_conn_complete3 test_wrap3) endif() if(P4EST_HAVE_GETOPT_H) - list(APPEND tests test_load3) + list(APPEND p8est_tests test_load3) endif() endif() if(enable_p6est AND enable_p8est AND P4EST_HAVE_GETOPT_H) -if(P4EST_HAVE_ARPA_INET_H OR P4EST_HAVE_NETINET_IN_H OR P4EST_HAVE_WINSOCK2_H) - # htonl - add_executable(p6est_test_all test_all6.c) - target_link_libraries(p6est_test_all PRIVATE p4est) - - if(P4EST_HAVE_WINSOCK2_H) - target_link_libraries(p6est_test_all PRIVATE ${WINSOCK_LIBRARIES}) - endif() - - if(MPI_FOUND) - add_test(NAME p6est:test_all COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${Ncpu} $) - else() - add_test(NAME p6est:test_all COMMAND $) + if(P4EST_HAVE_ARPA_INET_H OR P4EST_HAVE_NETINET_IN_H OR P4EST_HAVE_WINSOCK2_H) + set(p6est_tests test_all6) endif() - set_tests_properties(p6est:test_all PROPERTIES - TIMEOUT 60 - RESOURCE_LOCK cpu_mpi) -endif() endif() -foreach(t ${tests}) +foreach(t IN LISTS p4est_tests p6est_tests p8est_tests) add_executable(${t} ${t}.c) -target_link_libraries(${t} PRIVATE p4est) -if(MPI_FOUND) - add_test(NAME p4est:${t} COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${Ncpu} $) +target_link_libraries(${t} PRIVATE +p4est +$<$:${WINSOCK_LIBRARIES}> +) + +if(P4EST_ENABLE_MPI) + add_test(NAME ${t} COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROC_MAX} $) else() - add_test(NAME p4est:${t} COMMAND $) + add_test(NAME ${t} COMMAND ${t}) endif() -set_tests_properties(p4est:${t} PROPERTIES - TIMEOUT 60 - RESOURCE_LOCK cpu_mpi) endforeach() -get_property(_tests DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY TESTS) +set_property(TEST ${p4est_tests} ${p6est_tests} ${p8est_tests} PROPERTY TIMEOUT 60) + +if(P4EST_ENABLE_MPI) + set_property(TEST ${p4est_tests} ${p6est_tests} ${p8est_tests} PROPERTY RESOURCE_LOCK cpu_mpi) +endif() + +set_property(TEST ${p4est_tests} PROPERTY LABELS p4est) +set_property(TEST ${p6est_tests} PROPERTY LABELS p6est) +set_property(TEST ${p8est_tests} PROPERTY LABELS p8est) + +message(DEBUG "p4est tests: ${p4est_tests} +p6est tests: ${p6est_tests} +p8est tests: ${p8est_tests} +") + + +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.22) + if(WIN32) + set_property(TEST ${p4est_tests} ${p6est_tests} ${p8est_tests} PROPERTY + ENVIRONMENT_MODIFICATION "PATH=path_list_append:$" + ) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set_property(TEST ${p4est_tests} ${p6est_tests} ${p8est_tests} PROPERTY + ENVIRONMENT_MODIFICATION "LD_LIBRARY_PATH=path_list_append:$" + ) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set_property(TEST ${p4est_tests} ${p6est_tests} ${p8est_tests} PROPERTY + ENVIRONMENT_MODIFICATION "DYLD_LIBRARY_PATH=path_list_append:$" + ) + endif() +endif() -if(p4est:test_loadsave2 IN_LIST _tests) - set_tests_properties(p4est:test_loadsave2 PROPERTIES - TIMEOUT 600 - ) +if(test_loadsave2 IN_LIST p4est_tests) + set_property(TEST test_loadsave2 PROPERTY TIMEOUT 600) endif() diff --git a/test/Makefile.am b/test/Makefile.am index f9e85c22a..985c60d77 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -15,7 +15,7 @@ p4est_test_programs += \ test/p4est_test_mesh_bijective test/p4est_test_conn_transformation \ test/p4est_test_iterate test/p4est_test_lnodes \ test/p4est_test_search test/p4est_test_brick \ - test/p4est_test_complete_subtree \ + test/p4est_test_complete_subtree \ test/p4est_test_partition_corr \ test/p4est_test_conn_complete test/p4est_test_balance_seeds \ test/p4est_test_wrap test/p4est_test_replace test/p4est_test_join \ @@ -23,7 +23,9 @@ p4est_test_programs += \ test/p4est_test_connrefine \ test/p4est_test_subcomm \ test/p4est_test_nodes \ - test/p4est_test_version + test/p4est_test_version \ + test/p4est_test_io \ + test/p4est_test_neighbor_transform if P4EST_WITH_METIS p4est_test_programs += \ test/p4est_test_reorder @@ -38,6 +40,7 @@ p4est_test_programs += \ test/p8est_test_periodic test/p8est_test_loadsave \ test/p8est_test_load test/p8est_test_ghost \ test/p8est_test_mesh_bijective test/p8est_test_conn_transformation \ + test/p8est_test_mesh_corners \ test/p8est_test_iterate test/p8est_test_lnodes \ test/p8est_test_search test/p8est_test_brick \ test/p8est_test_partition_corr \ @@ -47,7 +50,9 @@ p4est_test_programs += \ test/p8est_test_connrefine \ test/p8est_test_subcomm \ test/p8est_test_nodes \ - test/p8est_test_version + test/p8est_test_version \ + test/p8est_test_io \ + test/p8est_test_neighbor_transform if P4EST_WITH_METIS p4est_test_programs += \ test/p8est_test_reorder @@ -99,7 +104,9 @@ test_p4est_test_plex_SOURCES = test/test_plex2.c test_p4est_test_connrefine_SOURCES = test/test_connrefine2.c test_p4est_test_subcomm_SOURCES = test/test_subcomm2.c test_p4est_test_nodes_SOURCES = test/test_nodes2.c +test_p4est_test_neighbor_transform_SOURCES = test/test_neighbor_transform2.c test_p4est_test_version_SOURCES = test/test_version.c +test_p4est_test_io_SOURCES = test/test_io2.c if P4EST_WITH_METIS test_p4est_test_reorder_SOURCES = test/test_reorder2.c endif @@ -122,6 +129,7 @@ test_p8est_test_loadsave_SOURCES = test/test_loadsave3.c test_p8est_test_load_SOURCES = test/test_load3.c test_p8est_test_ghost_SOURCES = test/test_ghost3.c test_p8est_test_mesh_bijective_SOURCES = test/test_mesh_bijective3.c +test_p8est_test_mesh_corners_SOURCES = test/test_mesh_corners3.c test_p8est_test_conn_transformation_SOURCES = test/test_conn_transformation3.c test_p8est_test_brick_SOURCES = test/test_brick3.c test_p8est_test_iterate_SOURCES = test/test_iterate3.c @@ -138,7 +146,9 @@ test_p8est_test_plex_SOURCES = test/test_plex3.c test_p8est_test_connrefine_SOURCES = test/test_connrefine3.c test_p8est_test_subcomm_SOURCES = test/test_subcomm3.c test_p8est_test_nodes_SOURCES = test/test_nodes3.c +test_p8est_test_neighbor_transform_SOURCES = test/test_neighbor_transform3.c test_p8est_test_version_SOURCES = test/test_version.c +test_p8est_test_io_SOURCES = test/test_io3.c if P4EST_WITH_METIS test_p8est_test_reorder_SOURCES = test/test_reorder3.c endif @@ -156,73 +166,3 @@ endif endif TESTS += $(p4est_test_programs) - -LINT_CSOURCES += \ - $(test_p4est_test_comm_SOURCES) \ - $(test_p4est_test_hash_SOURCES) \ - $(test_p4est_test_quadrants_SOURCES) \ - $(test_p4est_test_balance_SOURCES) \ - $(test_p4est_test_partition_SOURCES) \ - $(test_p4est_test_order_SOURCES) \ - $(test_p4est_test_coarsen_SOURCES) \ - $(test_p4est_test_valid_SOURCES) \ - $(test_p4est_test_balance_type_SOURCES) \ - $(test_p4est_test_loadsave_SOURCES) \ - $(test_p4est_test_load_SOURCES) \ - $(test_p4est_test_ghost_SOURCES) \ - $(test_p4est_test_mesh_bijective_SOURCES) \ - $(test_p4est_test_conn_transformation_SOURCES) \ - $(test_p4est_test_iterate_SOURCES) \ - $(test_p4est_test_lnodes_SOURCES) \ - $(test_p4est_test_search_SOURCES) \ - $(test_p4est_test_complete_subtree_SOURCES) \ - $(test_p4est_test_brick_SOURCES) \ - $(test_p4est_test_partition_corr_SOURCES) \ - $(test_p4est_test_reorder_SOURCES) \ - $(test_p4est_test_balance_seeds_SOURCES) \ - $(test_p4est_test_wrap_SOURCES) \ - $(test_p4est_test_replace_SOURCES) \ - $(test_p4est_test_join_SOURCES) \ - $(test_p4est_test_conn_reduce_SOURCES) \ - $(test_p4est_test_plex_SOURCES) \ - $(test_p4est_test_connrefine_SOURCES) \ - $(test_p4est_test_subcomm_SOURCES) \ - $(test_p4est_test_nodes_SOURCES) \ - $(test_p4est_test_version_SOURCES) \ - $(test_p8est_test_quadrants_SOURCES) \ - $(test_p8est_test_balance_SOURCES) \ - $(test_p8est_test_partition_SOURCES) \ - $(test_p8est_test_coarsen_SOURCES) \ - $(test_p8est_test_valid_SOURCES) \ - $(test_p8est_test_balance_type_SOURCES) \ - $(test_p8est_test_face_transform_SOURCES) \ - $(test_p8est_test_edge_face_corners_SOURCES) \ - $(test_p8est_test_periodic_SOURCES) \ - $(test_p8est_test_loadsave_SOURCES) \ - $(test_p8est_test_load_SOURCES) \ - $(test_p8est_test_ghost_SOURCES) \ - $(test_p8est_test_mesh_bijective_SOURCES) \ - $(test_p8est_test_conn_transformation_SOURCES) \ - $(test_p8est_test_brick_SOURCES) \ - $(test_p8est_test_iterate_SOURCES) \ - $(test_p8est_test_lnodes_SOURCES) \ - $(test_p8est_test_search_SOURCES) \ - $(test_p8est_test_reorder_SOURCES) \ - $(test_p8est_test_partition_corr_SOURCES) \ - $(test_p8est_test_balance_seeds_SOURCES) \ - $(test_p8est_test_wrap_SOURCES) \ - $(test_p8est_test_replace_SOURCES) \ - $(test_p8est_test_join_SOURCES) \ - $(test_p8est_test_conn_reduce_SOURCES) \ - $(test_p8est_test_plex_SOURCES) \ - $(test_p8est_test_connrefine_SOURCES) \ - $(test_p8est_test_subcomm_SOURCES) \ - $(test_p8est_test_nodes_SOURCES) \ - $(test_p8est_test_version_SOURCES) \ - $(test_p6est_test_all_SOURCES) - -if P4EST_WITH_METIS -LINT_CSOURCES += \ - $(test_p4est_test_reorder_SOURCES) \ - $(test_p8est_test_reorder_SOURCES) -endif diff --git a/test/test_all6.c b/test/test_all6.c index 0737b2551..1ebd2e5c9 100644 --- a/test/test_all6.c +++ b/test/test_all6.c @@ -171,9 +171,8 @@ main (int argc, char **argv) double height[3] = { 0., 0., 0.1 }; int i; int vtk; -#ifdef P4EST_HAVE_ZLIB + int have_zlib; unsigned crc_computed = 0; -#endif sc_options_t *opt; int first_argc; const char *config_name; @@ -189,6 +188,7 @@ main (int argc, char **argv) sc_set_log_defaults (NULL, NULL, SC_LP_STATISTICS); #endif p4est_init (NULL, SC_LP_DEFAULT); + have_zlib = p4est_have_zlib (); opt = sc_options_new (argv[0]); @@ -255,7 +255,7 @@ main (int argc, char **argv) sc_stats_set1 (&stats[TIMINGS_REFINE_LAYERS], snapshot.iwtime, "Refine layers"); - refine_level += 2; + refine_level += 1; sc_flops_snap (&fi, &snapshot); p6est_refine_columns (p6est, 1, refine_column_fn, init_fn); sc_flops_shot (&fi, &snapshot); @@ -379,11 +379,11 @@ main (int argc, char **argv) p6est_lnodes_destroy (lnodes); } -#ifdef P4EST_HAVE_ZLIB - crc_computed = p6est_checksum (p6est); - - P4EST_GLOBAL_PRODUCTIONF ("p6est checksum 0x%08x\n", crc_computed); -#endif + if (have_zlib) { + /* the parallel checksum aborts without zlib configured */ + crc_computed = p6est_checksum (p6est); + P4EST_GLOBAL_PRODUCTIONF ("p6est checksum 0x%08x\n", crc_computed); + } if (save_filename) { sc_flops_snap (&fi, &snapshot); diff --git a/test/test_balance2.c b/test/test_balance2.c index cc9f7aa66..42da9b5c9 100644 --- a/test/test_balance2.c +++ b/test/test_balance2.c @@ -86,12 +86,19 @@ refine_fn (p4est_t * p4est, p4est_topidx_t which_tree, return 1; } +static unsigned +test_checksum (p4est_t * p4est, int have_zlib) +{ + return have_zlib ? p4est_checksum (p4est) : 0; +} + int main (int argc, char **argv) { sc_MPI_Comm mpicomm; int mpiret; int mpisize, mpirank; + int have_zlib; unsigned crc; #ifndef P4_TO_P8 size_t kz; @@ -111,9 +118,16 @@ main (int argc, char **argv) mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank); SC_CHECK_MPI (mpiret); + /* establish parallel logging */ sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT); p4est_init (NULL, SC_LP_DEFAULT); + /* check for ZLIB usability */ + if (!(have_zlib = p4est_have_zlib ())) { + P4EST_GLOBAL_LERROR + ("Not found a working ZLIB installation: ignoring CRCs\n"); + } + #ifndef P4_TO_P8 connectivity = p4est_connectivity_new_star (); #else @@ -175,9 +189,9 @@ main (int argc, char **argv) p4est_reset_data (p4est, 8, init_fn, NULL); /* checksum and partition */ - crc = p4est_checksum (p4est); + crc = test_checksum (p4est, have_zlib); p4est_partition (p4est, 0, NULL); - SC_CHECK_ABORT (p4est_checksum (p4est) == crc, "Partition"); + SC_CHECK_ABORT (test_checksum (p4est, have_zlib) == crc, "Partition"); SC_CHECK_ABORT (p4est_is_balanced (p4est, P4EST_CONNECT_FULL), "Balance 4"); /* check reset data function */ @@ -185,9 +199,9 @@ main (int argc, char **argv) p4est_reset_data (p4est, 3, NULL, NULL); /* checksum and rebalance */ - crc = p4est_checksum (p4est); + crc = test_checksum (p4est, have_zlib); p4est_balance (p4est, P4EST_CONNECT_FULL, NULL); - SC_CHECK_ABORT (p4est_checksum (p4est) == crc, "Rebalance"); + SC_CHECK_ABORT (test_checksum (p4est, have_zlib) == crc, "Rebalance"); /* clean up and exit */ P4EST_ASSERT (p4est->user_data_pool->elem_count == diff --git a/test/test_balance_seeds2.c b/test/test_balance_seeds2.c index a0ccf17ff..12f5a2a54 100644 --- a/test/test_balance_seeds2.c +++ b/test/test_balance_seeds2.c @@ -102,7 +102,7 @@ check_balance_seeds (p4est_quadrant_t * q, p4est_quadrant_t * p, sc_array_resize (seeds, 0); - s = (p4est_quadrant_t *) sc_array_push (thislevel); + s = p4est_quadrant_array_push (thislevel); p4est_quadrant_sibling (q, s, 0); #ifndef P4_TO_P8 @@ -146,7 +146,7 @@ check_balance_seeds (p4est_quadrant_t * q, p4est_quadrant_t * p, p4est_quadrant_sibling (&temp2, t, 0); } else if (p4est_quadrant_is_inside_root (&temp2)) { - t = (p4est_quadrant_t *) sc_array_push (nextlevel); + t = p4est_quadrant_array_push (nextlevel); p4est_quadrant_sibling (&temp2, t, 0); } } @@ -168,7 +168,7 @@ check_balance_seeds (p4est_quadrant_t * q, p4est_quadrant_t * p, p4est_quadrant_sibling (&temp2, t, 0); } else if (p4est_quadrant_is_inside_root (&temp2)) { - t = (p4est_quadrant_t *) sc_array_push (nextlevel); + t = p4est_quadrant_array_push (nextlevel); p4est_quadrant_sibling (&temp2, t, 0); } } @@ -190,7 +190,7 @@ check_balance_seeds (p4est_quadrant_t * q, p4est_quadrant_t * p, p4est_quadrant_sibling (&temp2, t, 0); } else if (p4est_quadrant_is_inside_root (&temp2)) { - t = (p4est_quadrant_t *) sc_array_push (nextlevel); + t = p4est_quadrant_array_push (nextlevel); p4est_quadrant_sibling (&temp2, t, 0); } } diff --git a/test/test_balance_type2.c b/test/test_balance_type2.c index 3caf7c189..017fad095 100644 --- a/test/test_balance_type2.c +++ b/test/test_balance_type2.c @@ -77,11 +77,18 @@ refine_fn (p4est_t * p4est, p4est_topidx_t which_tree, return 1; } +static unsigned +test_checksum (p4est_t * p4est, int have_zlib) +{ + return have_zlib ? p4est_checksum (p4est) : 0; +} + int main (int argc, char **argv) { sc_MPI_Comm mpicomm; int mpiret; + int have_zlib; int size, rank; unsigned crcF, crcC; p4est_connectivity_t *connectivity; @@ -101,9 +108,16 @@ main (int argc, char **argv) mpiret = sc_MPI_Comm_rank (mpicomm, &rank); SC_CHECK_MPI (mpiret); + /* establish parallel logging */ sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT); p4est_init (NULL, SC_LP_DEFAULT); + /* check for ZLIB usability */ + if (!(have_zlib = p4est_have_zlib ())) { + P4EST_GLOBAL_LERROR + ("Not found a working ZLIB installation: ignoring CRCs\n"); + } + /* create forest and refine */ #ifndef P4_TO_P8 connectivity = p4est_connectivity_new_star (); @@ -120,7 +134,7 @@ main (int argc, char **argv) #else p4est_balance (p4estF, P8EST_CONNECT_FACE, NULL); #endif - crcF = p4est_checksum (p4estF); + crcF = test_checksum (p4estF, have_zlib); P4EST_GLOBAL_INFOF ("Face balance with %lld quadrants and crc 0x%08x\n", (long long) p4estF->global_num_quadrants, crcF); @@ -129,8 +143,8 @@ main (int argc, char **argv) p4estE = p4est_copy (p4est, 1); p4est_balance (p4estF, P8EST_CONNECT_EDGE, NULL); p4est_balance (p4estE, P8EST_CONNECT_EDGE, NULL); - crcE = p4est_checksum (p4estE); - SC_CHECK_ABORT (crcE == p4est_checksum (p4estF), "mismatch A"); + crcE = test_checksum (p4estE, have_zlib); + SC_CHECK_ABORT (crcE == test_checksum (p4estF, have_zlib), "mismatch A"); P4EST_GLOBAL_INFOF ("Edge balance with %lld quadrants and crc 0x%08x\n", (long long) p4estE->global_num_quadrants, crcE); #endif @@ -144,8 +158,8 @@ main (int argc, char **argv) p4est_balance (p4estF, P8EST_CONNECT_CORNER, NULL); p4est_balance (p4estC, P8EST_CONNECT_CORNER, NULL); #endif - crcC = p4est_checksum (p4estC); - SC_CHECK_ABORT (crcC == p4est_checksum (p4estF), "mismatch B"); + crcC = test_checksum (p4estC, have_zlib); + SC_CHECK_ABORT (crcC == test_checksum (p4estF, have_zlib), "mismatch B"); P4EST_GLOBAL_INFOF ("Corner balance with %lld quadrants and crc 0x%08x\n", (long long) p4estC->global_num_quadrants, crcC); diff --git a/test/test_complete_subtree.c b/test/test_complete_subtree.c index 98ae96d60..b6aa3321f 100644 --- a/test/test_complete_subtree.c +++ b/test/test_complete_subtree.c @@ -119,7 +119,7 @@ test_build_local (sc_MPI_Comm mpicomm) P4EST_ASSERT (p4est_quadrant_is_valid (quadrant)); /* add to emptied subtree */ - *(p4est_quadrant_t *) sc_array_push (&subtree->quadrants) = *quadrant; + (void) p4est_quadrant_array_push_copy (&subtree->quadrants, quadrant); subtree->quadrants_per_level[quadrant->level] = 1; subtree->maxlevel = quadrant->level; diff --git a/test/test_ghost2.c b/test/test_ghost2.c index 226f7e41a..9c7f96b6f 100644 --- a/test/test_ghost2.c +++ b/test/test_ghost2.c @@ -433,7 +433,7 @@ main (int argc, char **argv) } p4est_ghost_destroy (ghost); - /* repeate the cyle, but with lnodes */ + /* repeat the cycle, but with lnodes */ /* create the ghost layer */ ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL); type = p4est_connect_type_int (ghost->btype); diff --git a/test/test_hash.c b/test/test_hash.c index 804be0f3c..e17a59551 100644 --- a/test/test_hash.c +++ b/test/test_hash.c @@ -27,13 +27,13 @@ static unsigned int_hash_fn (const void *v, const void *u) { - return (unsigned) (unsigned long) v; + return (unsigned) (size_t) v; } static int int_equal_fn (const void *v1, const void *v2, const void *u) { - return (long) v1 == (long) v2; + return (size_t) v1 == (size_t) v2; } static void @@ -64,7 +64,7 @@ main (int argc, char **argv) inserted = 0; for (i = 0; i < 347; ++i) { inserted += - sc_hash_insert_unique (ihash, (void *) ((long) i % 91), NULL); + sc_hash_insert_unique (ihash, (void *) ((size_t) i % 91), NULL); } P4EST_VERBOSEF ("Integers inserted %d total %llu\n", inserted, (unsigned long long) ihash->elem_count); diff --git a/test/test_io2.c b/test/test_io2.c new file mode 100644 index 000000000..9668f5e5b --- /dev/null +++ b/test/test_io2.c @@ -0,0 +1,646 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef P4_TO_P8 +#include +#include +#include +#else +#include +#include +#include +#endif +#include + +#ifdef P4EST_ENABLE_FILE_DEPRECATED + +#ifndef P4_TO_P8 +#define P4EST_DATA_FILE_EXT "p4d" /**< file extension of p4est data files */ +#else +#define P4EST_DATA_FILE_EXT P8EST_DATA_FILE_EXT +#define P8EST_DATA_FILE_EXT "p8d" /**< file extension of p8est data files */ +#endif + +#define P4EST_INVALID_FILE "test_io_invalid" +#define HEADER_INT1 42 +#define HEADER_INT2 84 + +static void +write_block (int *header) +{ + header[0] = HEADER_INT1; + header[1] = HEADER_INT2; +} + +static int +refine (p4est_t * p4est, p4est_topidx_t which_tree, + p4est_quadrant_t * quadrant) +{ + return quadrant->x == 0 && quadrant->y == 0 +#ifdef P4_TO_P8 + && quadrant->z == 0 +#endif + ; +} + +static void +write_chars (p4est_t * p4est, sc_array_t * quad_data) +{ + p4est_locidx_t i; + char *current; + char char_to_write = + (char) (p4est->global_num_quadrants % 97); + + for (i = 0; i < p4est->local_num_quadrants; ++i) { + current = (char *) sc_array_index (quad_data, i); + *current = char_to_write; + } +} + +static void +parse_file_metadata (p4est_t * p4est, const char *filename) +{ + int mpiret, ecode, msglen; + sc_array_t data_sizes; + char msg[sc_MPI_MAX_ERROR_STRING]; + char user_string[P4EST_FILE_USER_STRING_BYTES]; + + P4EST_GLOBAL_PRODUCTIONF ("Parse %s\n", filename); + + sc_array_init (&data_sizes, sizeof (p4est_file_section_metadata_t)); + p4est_file_info (p4est, filename, user_string, &data_sizes, &ecode); + + /* check error code for correctly reported errors */ + if (!strcmp (filename, P4EST_INVALID_FILE "0." P4EST_DATA_FILE_EXT)) { + SC_CHECK_ABORT (ecode == P4EST_FILE_ERR_FORMAT, + "Error code for " P4EST_INVALID_FILE "0"); + } + else if (!strcmp (filename, P4EST_INVALID_FILE "1." P4EST_DATA_FILE_EXT)) { + SC_CHECK_ABORT (ecode == P4EST_FILE_ERR_SUCCESS, + "Error code for " P4EST_INVALID_FILE "1"); + } + else if (!strcmp (filename, P4EST_INVALID_FILE "2." P4EST_DATA_FILE_EXT)) { + SC_CHECK_ABORT (ecode == P4EST_FILE_ERR_FORMAT, + "Error code for " P4EST_INVALID_FILE "2"); + } + else if (!strcmp (filename, P4EST_INVALID_FILE "3." P4EST_DATA_FILE_EXT)) { + SC_CHECK_ABORT (ecode == P4EST_FILE_ERR_FORMAT, + "Error code for " P4EST_INVALID_FILE "3"); + } + + mpiret = p4est_file_error_string (ecode, msg, &msglen); + SC_CHECK_MPI (mpiret); + P4EST_GLOBAL_LERRORF ("file_info of %s at %s:%d: %s\n", + filename, __FILE__, __LINE__, msg); + sc_array_reset (&data_sizes); +} + +/** Write some invalid files in serial to the disk to check + * the error handling of the p4est_file_* functions. + */ +static void +write_invalid_files (p4est_t * p4est) +{ + if (p4est->mpirank == 0) { + char string0[P4EST_FILE_METADATA_BYTES + 1]; + FILE *file; + int ret; + + /* invalid0 */ + snprintf (string0, P4EST_FILE_METADATA_BYTES + 1, + "%.7s\n%-23s\n%-47s\n%.16lld", "p4data1", + p4est_version (), "invalid0", + (long long) p4est->global_num_quadrants); + string0[P4EST_FILE_METADATA_BYTES] = '\0'; + + file = fopen (P4EST_INVALID_FILE "0." P4EST_DATA_FILE_EXT, "w"); + ret = fprintf (file, "%s", string0); + if ((size_t) ret != strlen (string0)) { + P4EST_LERROR ("Could not write" P4EST_INVALID_FILE "0." + P4EST_DATA_FILE_EXT); + } + fclose (file); + + /* invalid1 */ + snprintf (string0, P4EST_FILE_METADATA_BYTES + 1, + "%.7s\n%-23s\n%-47s\n%.16lld", P4EST_FILE_MAGIC_NUMBER, + "A wrong version string", "invalid1", + (long long) p4est->global_num_quadrants); + string0[P4EST_FILE_METADATA_BYTES] = '\0'; + + file = fopen (P4EST_INVALID_FILE "1." P4EST_DATA_FILE_EXT, "w"); + ret = fprintf (file, "%s", string0); + if ((size_t) ret != strlen (string0)) { + P4EST_LERROR ("Could not write" P4EST_INVALID_FILE "1." + P4EST_DATA_FILE_EXT); + } + fclose (file); + + /* invalid2 */ + snprintf (string0, P4EST_FILE_METADATA_BYTES + 1, + "%.7s\n%-23s\n%-48s%.16lld", P4EST_FILE_MAGIC_NUMBER, + p4est_version (), "invalid2", + (long long) p4est->global_num_quadrants); + string0[P4EST_FILE_METADATA_BYTES] = '\0'; + + file = fopen (P4EST_INVALID_FILE "2." P4EST_DATA_FILE_EXT, "w"); + ret = fprintf (file, "%s", string0); + if ((size_t) ret != strlen (string0)) { + P4EST_LERROR ("Could not write" P4EST_INVALID_FILE "2." + P4EST_DATA_FILE_EXT); + } + fclose (file); + + /* invalid3 */ + snprintf (string0, P4EST_FILE_METADATA_BYTES + 1, + "%.7s\n%-23s\n%-47s\n%.16ld", P4EST_FILE_MAGIC_NUMBER, + p4est_version (), "invalid3", 8L); + string0[P4EST_FILE_METADATA_BYTES] = '\0'; + + file = fopen (P4EST_INVALID_FILE "3." P4EST_DATA_FILE_EXT, "w"); + ret = fprintf (file, "%s", string0); + if ((size_t) ret != strlen (string0)) { + P4EST_LERROR ("Could not write" P4EST_INVALID_FILE "3." + P4EST_DATA_FILE_EXT); + } + fclose (file); + } + parse_file_metadata (p4est, P4EST_INVALID_FILE "0." P4EST_DATA_FILE_EXT); + parse_file_metadata (p4est, P4EST_INVALID_FILE "1." P4EST_DATA_FILE_EXT); + parse_file_metadata (p4est, P4EST_INVALID_FILE "2." P4EST_DATA_FILE_EXT); + parse_file_metadata (p4est, P4EST_INVALID_FILE "3." P4EST_DATA_FILE_EXT); +} + +/** A data structure to store compressed quadrants. + */ +typedef struct compressed_quadrant +{ + p4est_qcoord_t x, y; +#ifdef P4_TO_P8 + p4est_qcoord_t z; +#endif + p4est_qcoord_t level; +} +compressed_quadrant_t; + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + +int +main (int argc, char **argv) +{ +#ifdef P4EST_ENABLE_FILE_DEPRECATED + + sc_MPI_Comm mpicomm; + int mpiret, errcode; + int rank, size; + int level = 3; + int empty_header, read_only, header_only; + char *current_char; + int header_size = 8; + size_t si; + p4est_file_section_metadata_t current_elem; + int header[2], read_header[2]; + char msg[sc_MPI_MAX_ERROR_STRING]; + int msglen; + unsigned checksum; + p4est_connectivity_t *connectivity; + p4est_t *p4est; + p4est_file_context_t *fc, *fc1; + p4est_locidx_t i; + size_t quadrant_size; + sc_array_t block_arr; + sc_array_t quad_data; + sc_array_t read_data, read_quads; + sc_array_t elem_size; + sc_array_t *quads = NULL; + sc_array_t unaligned; + sc_array_t empty; + sc_options_t *opt; + char current_user_string[P4EST_FILE_USER_STRING_BYTES]; + compressed_quadrant_t *qr, *qs; + + /* initialize MPI */ + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + mpicomm = sc_MPI_COMM_WORLD; + mpiret = sc_MPI_Comm_size (mpicomm, &size); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Comm_rank (mpicomm, &rank); + SC_CHECK_MPI (mpiret); + + sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT); + p4est_init (NULL, SC_LP_DEFAULT); + + /* parse command line parameters */ + opt = sc_options_new (argv[0]); + sc_options_add_bool (opt, 'E', "empty header", &empty_header, 0, + "Write no user header"); + sc_options_add_bool (opt, 'R', "read only", &read_only, 0, "Only reading"); + sc_options_add_bool (opt, 'H', "header only", &header_only, 0, + "Write only the header"); + sc_options_parse (p4est_package_id, SC_LP_DEFAULT, opt, argc, argv); + + if (empty_header) { + header_size = 0; + } + +#ifndef P4_TO_P8 + connectivity = p4est_connectivity_new_unitsquare (); +#else + connectivity = p8est_connectivity_new_unitcube (); +#endif + + p4est = p4est_new_ext (mpicomm, connectivity, 0, level, 1, 0, NULL, NULL); + + /* Test the data array padding by provoking a number of quadrants that + * is not divisible by 16. + */ + p4est_refine (p4est, 1, refine, NULL); + + write_invalid_files (p4est); + + /* initialize the header */ + write_block (header); + + if (!header_only) { + /* initialize quadrant data array */ + sc_array_init (&quad_data, sizeof (char)); + sc_array_resize (&quad_data, p4est->local_num_quadrants); + /* initialize unaligned array */ + sc_array_init (&unaligned, 3 * sizeof (char)); + sc_array_resize (&unaligned, p4est->local_num_quadrants); + + /* extract coordinates and level of the quadrants */ + quads = p4est_deflate_quadrants (p4est, NULL); + /* p4est_file_write_filed requires per rank local_num_quadrants many elements + * and therefore we group the data per local quadrant by type casting. + */ + quads->elem_size = sizeof (compressed_quadrant_t); + quads->elem_count = p4est->local_num_quadrants; + } + + if (!read_only) { + /* provoke an error by a too long user string; exactly one char too much */ + fc = p4est_file_open_create (p4est, "test_io." P4EST_DATA_FILE_EXT, + "123456789101112131415161718192021222324252627282", + &errcode); + SC_CHECK_ABORT (fc == NULL + && errcode == P4EST_FILE_ERR_IN_DATA, + "Detect too long user string"); + + fc = + p4est_file_open_create (p4est, "test_io." P4EST_DATA_FILE_EXT, + "Test data file", &errcode); + SC_CHECK_ABORT (fc != NULL, "Open create"); + + if (!header_only) { + write_chars (p4est, &quad_data); + SC_CHECK_ABORT (p4est_file_write_field + (fc, quad_data.elem_size, &quad_data, + "Quadrant-wise char", &errcode) != NULL, + "Write chars"); + + SC_CHECK_ABORT (p4est_file_write_field + (fc, quads->elem_size, quads, "Quadrant data", &errcode) + != NULL, "Write quadrants"); + + for (i = 0; i < p4est->local_num_quadrants; ++i) { + current_char = (char *) sc_array_index (&unaligned, i); + current_char[0] = 'a'; + current_char[1] = 'b'; + current_char[2] = 'c'; + } + + SC_CHECK_ABORT (p4est_file_write_field + (fc, unaligned.elem_size, &unaligned, + "Data that needs to be padded", &errcode) != NULL, + "Write unaligned"); + + sc_array_init_data (&block_arr, header, header_size, 1); + SC_CHECK_ABORT (p4est_file_write_block + (fc, (size_t) header_size, &block_arr, + "Header as a block", &errcode) != NULL, + "Write header"); + + checksum = p4est_checksum (p4est); + + sc_array_init_data (&block_arr, &checksum, sizeof (unsigned), 1); +/* *INDENT-OFF* */ + SC_CHECK_ABORT (p4est_file_write_block (fc, sizeof (unsigned), + &block_arr, "p4est checksum", + &errcode) != NULL, + "Write forest checksum"); +/* *INDENT-ON* */ + + empty.elem_count = 1; + empty.elem_size = 0; + /* write an empty header block */ + SC_CHECK_ABORT (p4est_file_write_block (fc, 0, + &empty, "Empty data block", + &errcode) != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + "Write empty data block"); + + /* initialize empty sc_array */ + empty.elem_size = 0; + empty.elem_count = 0; + empty.byte_alloc = 0; + empty.array = NULL; + SC_CHECK_ABORT (p4est_file_write_field + (fc, empty.elem_size, &empty, "Empty data field", + &errcode) != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, + "Write empty data field"); + + } + + SC_CHECK_ABORT (p4est_file_close (fc, &errcode) == 0, + "Close file context 1"); + } + + if (!header_only) { + /* initialize read quadrant data array */ + sc_array_init (&read_data, sizeof (char)); + + fc = + p4est_file_open_read (p4est, "test_io." P4EST_DATA_FILE_EXT, + current_user_string, &errcode); + SC_CHECK_ABORT (fc != NULL, "Open read 1"); + P4EST_GLOBAL_PRODUCTIONF ("Read file with user string: %s\n", + current_user_string); + + /* Try to open a non-existent file to test the error code/class. */ + fc1 = + p4est_file_open_read (p4est, "test_iot." P4EST_DATA_FILE_EXT, + current_user_string, &errcode); + mpiret = p4est_file_error_string (errcode, msg, &msglen); + SC_CHECK_MPI (mpiret); + P4EST_GLOBAL_LERRORF ("Intended error by opening a non-existing" + " file (but we can not guarantee non-existence)" + " at %s:%d: %s\n", __FILE__, __LINE__, msg); + if (fc1 != NULL) { + /* the file seems to be existent by accident */ + SC_CHECK_ABORT (p4est_file_close (fc1, &errcode) == 0, + "Close accidentally opened file"); + } + + /* read the first data array */ + SC_CHECK_ABORT (p4est_file_read_field + (fc, read_data.elem_size, &read_data, current_user_string, + &errcode) != NULL, "Read chars"); + P4EST_GLOBAL_PRODUCTIONF ("Read data with user string: %s\n", + current_user_string); + + /* read the second data array */ + sc_array_init (&read_quads, sizeof (compressed_quadrant_t)); + SC_CHECK_ABORT (p4est_file_read_field + (fc, read_quads.elem_size, &read_quads, + current_user_string, &errcode) != NULL, + "Read quadrants"); + P4EST_GLOBAL_PRODUCTIONF ("Read data with user string: %s\n", + current_user_string); + + SC_CHECK_ABORT (p4est_file_close (fc, &errcode) == 0, + "Close file context 2"); + + /* check the read data */ + for (i = 0; i < p4est->local_num_quadrants; ++i) { + P4EST_ASSERT (read_quads.elem_count == quads->elem_count); + qr = (compressed_quadrant_t *) sc_array_index (&read_quads, i); + qs = (compressed_quadrant_t *) sc_array_index (quads, i); +#ifdef P4_TO_P8 + SC_CHECK_ABORT (qr->x == qs->x && qr->y == qs->y + && qr->z == qs->z + && qr->level == qs->level, "Quadrant read"); +#else + SC_CHECK_ABORT (qr->x == qs->x && qr->y == qs->y + && qr->level == qs->level, "Quadrant read"); +#endif + } + + /* check read data of the first array */ + for (i = 0; i < p4est->local_num_quadrants; ++i) { + current_char = (char *) sc_array_index (&read_data, i); + SC_CHECK_ABORT (*current_char == + (char) (p4est->global_num_quadrants % 97), "Char read"); + } + + } + + sc_array_init (&elem_size, sizeof (p4est_file_section_metadata_t)); + SC_CHECK_ABORT (p4est_file_info + (p4est, "test_io." P4EST_DATA_FILE_EXT, current_user_string, + &elem_size, &errcode) == 0, "Get file info"); + P4EST_GLOBAL_PRODUCTIONF + ("file info: number of global quadrants = %lld, number of data sections = %lld, user string = %s\n", + (long long) p4est->global_num_quadrants, + (unsigned long long) elem_size.elem_count, current_user_string); + SC_CHECK_ABORT (elem_size.elem_count == ((!empty_header) ? 7 : 6), + "file_info: number of data sections"); + for (si = 0; si < elem_size.elem_count; ++si) { + current_elem = + *(p4est_file_section_metadata_t *) sc_array_index (&elem_size, si); + P4EST_GLOBAL_PRODUCTIONF + ("Array %ld: section type %c, element size %ld, section string %s\n", + si, current_elem.block_type, current_elem.data_size, + current_elem.user_string); + } + + if (!header_only) { + /* zero unaligned array */ + for (i = 0; i < p4est->local_num_quadrants; ++i) { + current_char = (char *) sc_array_index (&unaligned, i); + current_char[0] = '\0'; + current_char[1] = '\0'; + current_char[2] = '\0'; + } + } + + if (!header_only) { + fc = + p4est_file_open_read (p4est, "test_io." P4EST_DATA_FILE_EXT, + current_user_string, &errcode); + SC_CHECK_ABORT (fc != NULL, "Open read 2"); + P4EST_GLOBAL_PRODUCTIONF ("Read file with user string: %s\n", + current_user_string); + + /* skip two data arrays */ + /* Although we skip the data it is required to pass the correct data size */ + SC_CHECK_ABORT (p4est_file_read_field + (fc, 1, NULL, current_user_string, &errcode) == fc + && errcode == P4EST_FILE_ERR_SUCCESS, "Read skip 1"); + P4EST_GLOBAL_PRODUCTIONF ("Skip data with user string: %s\n", + current_user_string); +#ifndef P4_TO_P8 + quadrant_size = 12; +#else + quadrant_size = 16; +#endif + SC_CHECK_ABORT (p4est_file_read_field + (fc, quadrant_size, NULL, current_user_string, + &errcode) == fc + && errcode == P4EST_FILE_ERR_SUCCESS, "Read skip 2"); + P4EST_GLOBAL_PRODUCTIONF ("Skip data with user string: %s\n", + current_user_string); + SC_CHECK_ABORT (p4est_file_read_field + (fc, unaligned.elem_size, &unaligned, current_user_string, + &errcode) != NULL + && errcode == P4EST_FILE_ERR_SUCCESS, "Read unaligned"); + P4EST_GLOBAL_PRODUCTIONF ("Read data with user string: %s\n", + current_user_string); + + for (i = 0; i < p4est->local_num_quadrants; ++i) { + current_char = (char *) sc_array_index (&unaligned, i); + SC_CHECK_ABORT (current_char[0] == 'a' && + current_char[1] == 'b' && + current_char[2] == 'c', "Read after array padding"); + } + + if (!empty_header) { + read_header[0] = -1; + read_header[1] = -1; + sc_array_init_data (&block_arr, read_header, header_size, 1); + SC_CHECK_ABORT (p4est_file_read_block + (fc, header_size, &block_arr, current_user_string, + &errcode) + != NULL, "Read header block"); + P4EST_GLOBAL_PRODUCTIONF ("Read header with user string: %s\n", + current_user_string); + /* check read content of the header block */ + SC_CHECK_ABORT (read_header[0] == 42 + && read_header[1] == 84, "Read header block"); + } + + SC_CHECK_ABORT (p4est_file_close (fc, &errcode) == 0, + "Close file context 3"); + + /* read and check the forest checksum */ + fc = + p4est_file_open_read (p4est, "test_io." P4EST_DATA_FILE_EXT, + current_user_string, &errcode); + SC_CHECK_ABORT (fc != NULL, "Open read 3"); + P4EST_GLOBAL_PRODUCTIONF ("Read file with user string: %s\n", + current_user_string); + + /* skip three data fields and one header block */ + SC_CHECK_ABORT (p4est_file_read_field + (fc, 1, NULL, current_user_string, &errcode) == fc + && errcode == P4EST_FILE_ERR_SUCCESS, "Read skip 1"); + P4EST_GLOBAL_PRODUCTIONF ("Skip data with user string: %s\n", + current_user_string); +#ifndef P4_TO_P8 + quadrant_size = 12; +#else + quadrant_size = 16; +#endif + SC_CHECK_ABORT (p4est_file_read_field + (fc, quadrant_size, NULL, current_user_string, + &errcode) == fc + && errcode == P4EST_FILE_ERR_SUCCESS, "Read skip 2"); + P4EST_GLOBAL_PRODUCTIONF ("Skip data with user string: %s\n", + current_user_string); + SC_CHECK_ABORT (p4est_file_read_field + (fc, 3, NULL, current_user_string, &errcode) == fc + && errcode == P4EST_FILE_ERR_SUCCESS, "Read skip 3"); + P4EST_GLOBAL_PRODUCTIONF ("Skip data with user string: %s\n", + current_user_string); + if (!empty_header) { + SC_CHECK_ABORT (p4est_file_read_block + (fc, 2 * sizeof (int), NULL, current_user_string, + &errcode) == fc + && errcode == P4EST_FILE_ERR_SUCCESS, + "Read skip header 4"); + P4EST_GLOBAL_PRODUCTIONF ("Skip block data with user string: %s\n", + current_user_string); + } + + /* read the header containing the forest checksum */ + checksum = 1; + sc_array_init_data (&block_arr, &checksum, sizeof (unsigned), 1); + SC_CHECK_ABORT (p4est_file_read_block + (fc, sizeof (unsigned), &block_arr, current_user_string, + &errcode) != NULL, "Read checksum"); + P4EST_GLOBAL_PRODUCTIONF ("Read header data with user string: %s\n", + current_user_string); + + /* check the checksum */ + SC_CHECK_ABORT (p4est_checksum (p4est) == checksum, + "Forest checksum equality"); + + empty.elem_count = 1; + empty.elem_size = 0; + /* read the empty block data */ + SC_CHECK_ABORT (p4est_file_read_block + (fc, 0, &empty, current_user_string, + &errcode) != NULL, "Read empty block data"); + P4EST_GLOBAL_PRODUCTIONF + ("Read empty block section with user string: %s\n", + current_user_string); + SC_CHECK_ABORT (!strcmp + (current_user_string, + "Empty data block "), + "Read empty data block user string"); + + /* read the empty field section */ + empty.elem_size = 0; + SC_CHECK_ABORT (p4est_file_read_field + (fc, empty.elem_size, &empty, current_user_string, + &errcode) == fc + && errcode == P4EST_FILE_ERR_SUCCESS, + "Read empty field section"); + P4EST_GLOBAL_PRODUCTIONF + ("Read empty field section with user string: %s\n", + current_user_string); + SC_CHECK_ABORT (!strcmp + (current_user_string, + "Empty data field "), + "Read empty data block user string"); + + SC_CHECK_ABORT (p4est_file_close (fc, &errcode) == 0, + "Close file context 4"); + } + + /* clean up */ + p4est_destroy (p4est); + p4est_connectivity_destroy (connectivity); + + if (!header_only) { + sc_array_reset (&quad_data); + sc_array_reset (&read_data); + sc_array_destroy (quads); + sc_array_reset (&read_quads); + sc_array_reset (&unaligned); + } + sc_array_reset (&elem_size); + sc_options_destroy (opt); + + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + +#endif /* P4EST_ENABLE_FILE_DEPRECATED */ + + return 0; +} diff --git a/test/test_io3.c b/test/test_io3.c new file mode 100644 index 000000000..e2001b63b --- /dev/null +++ b/test/test_io3.c @@ -0,0 +1,26 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include "test_io2.c" diff --git a/test/test_loadsave2.c b/test/test_loadsave2.c index 8a639eac9..d6bd1a695 100644 --- a/test/test_loadsave2.c +++ b/test/test_loadsave2.c @@ -149,11 +149,18 @@ test_deflate (p4est_t * p4est) } } +static unsigned +test_checksum (p4est_t * p4est, int have_zlib) +{ + return have_zlib ? p4est_checksum (p4est) : 0; +} + static void test_loadsave (p4est_connectivity_t * connectivity, const char *prefix, sc_MPI_Comm mpicomm, int mpirank) { int mpiret, retval; + int have_zlib; unsigned csum, csum2; double elapsed, wtime; p4est_connectivity_t *conn2; @@ -166,6 +173,12 @@ test_loadsave (p4est_connectivity_t * connectivity, const char *prefix, snprintf (p4est_name, BUFSIZ, "%s.%s", prefix, P4EST_FOREST_SUFFIX); P4EST_GLOBAL_INFOF ("Using file names %s and %s\n", conn_name, p4est_name); + /* check for ZLIB usability */ + if (!(have_zlib = p4est_have_zlib ())) { + P4EST_GLOBAL_LERROR + ("Not found a working ZLIB installation: ignoring CRCs\n"); + } + p4est = p4est_new_ext (mpicomm, connectivity, 0, 0, 0, sizeof (int), init_fn, NULL); p4est_refine (p4est, 1, refine_fn, init_fn); @@ -222,7 +235,7 @@ test_loadsave (p4est_connectivity_t * connectivity, const char *prefix, /* partition and balance */ p4est_partition (p4est, 0, NULL); p4est_balance (p4est, P4EST_CONNECT_FULL, init_fn); - csum = p4est_checksum (p4est); + csum = test_checksum (p4est, have_zlib); sc_stats_set1 (stats + STATS_P4EST_ELEMS, (double) p4est->local_num_quadrants, "p4est elements"); @@ -267,7 +280,7 @@ test_loadsave (p4est_connectivity_t * connectivity, const char *prefix, p4est2 = p4est_load_ext (p4est_name, mpicomm, sizeof (int), 0, 1, 0, NULL, &conn2); elapsed = sc_MPI_Wtime () - wtime; - csum2 = p4est_checksum (p4est2); + csum2 = test_checksum (p4est2, have_zlib); sc_stats_set1 (stats + STATS_P4EST_LOAD4, elapsed, "p4est load 4"); SC_CHECK_ABORT (p4est_connectivity_is_equal (connectivity, conn2), diff --git a/test/test_mesh_corners3.c b/test/test_mesh_corners3.c new file mode 100644 index 000000000..5e798592b --- /dev/null +++ b/test/test_mesh_corners3.c @@ -0,0 +1,292 @@ +#include +#include +#include + +static int +refine (p8est_t *p4est, p4est_topidx_t which_tree, + p8est_quadrant_t *quadrant) +{ + int refine = 1; + if (quadrant->x == 0 && quadrant->y == 0 && quadrant->z == 0 && + which_tree == 3) { + refine = 0; + } + return refine; +} + +static int +check_corner (p4est_topidx_t qid, p4est_locidx_t c, p4est_locidx_t qtc, + p4est_locidx_t expected_qtc) +{ + if (qtc != expected_qtc) { + printf ("ERROR: qid %d corner %d should have neighbor %d, but has %d\n", + qid, c, expected_qtc, qtc); + return 1; + } + return 0; +} + +static int +check_corner_across_trees (p4est_topidx_t qid, p4est_locidx_t c, + p4est_locidx_t qtc, p4est_locidx_t expected_qtc, + p8est_mesh_t *mesh) +{ + int offset; + int cind; + p4est_locidx_t new_qtc; + + /* corner neighbor information for quadrants from different trees are stored + * in the corner_quad array and have to be accessed using corner_offset */ + cind = qtc - mesh->local_num_quadrants - mesh->ghost_num_quadrants; + P4EST_ASSERT (cind >= 0); + offset = *(int *) sc_array_index (mesh->corner_offset, cind); + new_qtc = *(p4est_locidx_t *) sc_array_index (mesh->corner_quad, offset); + + return check_corner (qid, c, new_qtc, expected_qtc); +} + +int +main (int argc, char **argv) +{ + int num_errors = 0; + sc_MPI_Comm mpicomm = sc_MPI_COMM_SELF; + p8est_connectivity_t *conn; + p8est_t *p8est; + p8est_ghost_t *ghost; + p8est_mesh_params_t params; + p8est_mesh_t *mesh; + p4est_locidx_t coarse_qid; + p4est_locidx_t lx_idx, ux_idx, ly_idx, uy_idx, uz_idx; + p4est_locidx_t qtq_lx, qtq_ux, qtq_ly, qtq_uy, qtq_uz; + p4est_locidx_t *qth_lx, *qth_ux, *qth_ly, *qth_uy, *qth_uz; + p4est_locidx_t qid1, qid2; + p4est_locidx_t c1, c2; + p4est_locidx_t qtc1, qtc2; + + sc_MPI_Init (&argc, &argv); + sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT); + p4est_init (NULL, SC_LP_DEFAULT); + + /* create a 2x2 brick that is uniformly refined to level 2 except for a level 1 + * quadrant with coordinates (0,0,0) in tree 3. At the edges of the coarse + * quadrant there are edgehanging-corner neighbors inside the tree as well as + * across tree faces and edges. */ + conn = p8est_connectivity_new_brick (2, 2, 1, 0, 0, 0); + p8est = p8est_new_ext (mpicomm, conn, 0, 1, 0, 0, NULL, NULL); + + /* refine all quadrants to level 2 except quadrant in tree 3 touching 0,0,0 */ + p8est_refine_ext (p8est, 0, 2, refine, NULL, NULL); + + ghost = p8est_ghost_new (p8est, P8EST_CONNECT_FULL); + + /* create mesh with edgehanging corner neighbor information */ + p8est_mesh_params_init (¶ms); + params.btype = P8EST_CONNECT_FULL; + params.edgehanging_corners = 1; + mesh = p8est_mesh_new_params (p8est, ghost, ¶ms); + P4EST_ASSERT (mesh->local_num_quadrants == 249); + + /* assuming that the coarse quadrant is zero in tree 3g */ + coarse_qid = 192; + + /* upper and lower x,y,z indices */ + lx_idx = P8EST_FACES * coarse_qid + 0; + ux_idx = P8EST_FACES * coarse_qid + 1; + ly_idx = P8EST_FACES * coarse_qid + 2; + uy_idx = P8EST_FACES * coarse_qid + 3; + uz_idx = P8EST_FACES * coarse_qid + 5; + + P4EST_ASSERT (mesh->quad_to_face[lx_idx] < 0); + P4EST_ASSERT (mesh->quad_to_face[ux_idx] < 0); + P4EST_ASSERT (mesh->quad_to_face[ly_idx] < 0); + P4EST_ASSERT (mesh->quad_to_face[uy_idx] < 0); + P4EST_ASSERT (mesh->quad_to_face[uz_idx] < 0); + + /* get the quadrants on upper x,y,z faces of the coarse quadrant */ + qtq_lx = mesh->quad_to_quad[lx_idx]; + qtq_ux = mesh->quad_to_quad[ux_idx]; + qtq_ly = mesh->quad_to_quad[ly_idx]; + qtq_uy = mesh->quad_to_quad[uy_idx]; + qtq_uz = mesh->quad_to_quad[uz_idx]; + + qth_lx = (p4est_locidx_t *) sc_array_index (mesh->quad_to_half, qtq_lx); + qth_ux = (p4est_locidx_t *) sc_array_index (mesh->quad_to_half, qtq_ux); + qth_ly = (p4est_locidx_t *) sc_array_index (mesh->quad_to_half, qtq_ly); + qth_uy = (p4est_locidx_t *) sc_array_index (mesh->quad_to_half, qtq_uy); + qth_uz = (p4est_locidx_t *) sc_array_index (mesh->quad_to_half, qtq_uz); + + /*** test intra tree corners ***/ + /* six pairs of corners with missing information */ + + /* two along upper x upper y edge of coarse quad */ + qid1 = qth_ux[1]; /* lower quad along edge on x face of coarse quad */ + qid2 = qth_uy[3]; /* upper quad along edge on y face of coarse quad */ + c1 = 6; /* qid1 should have neighbor on uz uy lx corner */ + c2 = 1; /* qid2 should have neighbor on lz ly ux corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner (qid1, c1, qtc1, qid2); + num_errors += check_corner (qid2, c2, qtc2, qid1); + + qid1 = qth_ux[3]; /* upper quad along edge on x face of coarse quad */ + qid2 = qth_uy[1]; /* lower quad along edge on y face of coarse quad */ + c1 = 2; /* qid1 should have neighbor on lz uy lx corner */ + c2 = 5; /* qid2 should have neighbor on uz ly ux corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner (qid1, c1, qtc1, qid2); + num_errors += check_corner (qid2, c2, qtc2, qid1); + + /* two along upper x upper z edge of coarse quad */ + qid1 = qth_ux[2]; /* lower quad along edge on x face of coarse quad */ + qid2 = qth_uz[3]; /* upper quad along edge on z face of coarse quad */ + c1 = 6; /* qid1 should have neighbor on uz uy lx corner */ + c2 = 1; /* qid2 should have neighbor on lz ly ux corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner (qid1, c1, qtc1, qid2); + num_errors += check_corner (qid2, c2, qtc2, qid1); + + qid1 = qth_ux[3]; /* upper quad along edge on x face */ + qid2 = qth_uz[1]; /* lower quad along edge on z face */ + c1 = 4; /* qid1 should have neighbor on uz ly lx corner */ + c2 = 3; /* qid2 should have neighbor on lz uy ux corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner (qid1, c1, qtc1, qid2); + num_errors += check_corner (qid2, c2, qtc2, qid1); + + /* two along upper y upper z edge of coarse quad */ + qid1 = qth_uy[2]; /* lower quad along edge on y face of coarse quad */ + qid2 = qth_uz[3]; /* upper quad along edge on z face of coarse quad */ + c1 = 5; /* qid1 should have neighbor on uz ly ux corner */ + c2 = 2; /* qid2 should have neighbor on lz uy lx corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner (qid1, c1, qtc1, qid2); + num_errors += check_corner (qid2, c2, qtc2, qid1); + + qid1 = qth_uy[3]; /* upper quad along edge on y face */ + qid2 = qth_uz[2]; /* lower quad along edge on z face */ + c1 = 4; /* qid1 should have neighbor on uz ly lx corner */ + c2 = 3; /* qid2 should have neighbor on lz uy ux corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner (qid1, c1, qtc1, qid2); + num_errors += check_corner (qid2, c2, qtc2, qid1); + + /*** test inter-tree corners across tree-face ***/ + /* eight pairs of corners with missing information */ + + /* two along lower x upper y edge of coarse quad (across face to tree 2) */ + qid1 = qth_lx[1]; /* lower quad along edge on x face of coarse quad */ + qid2 = qth_uy[2]; /* upper quad along edge on y face of coarse quad */ + c1 = 7; /* qid1 should have neighbor on uz uy ux corner */ + c2 = 0; /* qid2 should have neighbor on lz ly lx corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + qid1 = qth_lx[3]; /* upper quad along edge on x face of coarse quad */ + qid2 = qth_uy[0]; /* lower quad along edge on y face of coarse quad */ + c1 = 3; /* qid1 should have neighbor on lz uy ux corner */ + c2 = 4; /* qid2 should have neighbor on uz ly lx corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + /* two along lower x upper z edge of coarse quad (across face to tree 2) */ + qid1 = qth_lx[2]; /* lower quad along edge on x face of coarse quad */ + qid2 = qth_uz[2]; /* upper quad along edge on z face of coarse quad */ + c1 = 7; /* qid1 should have neighbor on uz uy ux corner */ + c2 = 0; /* qid2 should have neighbor on lz ly lx corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + qid1 = qth_lx[3]; /* upper quad along edge on x face of coarse quad */ + qid2 = qth_uz[0]; /* lower quad along edge on z face of coarse quad */ + c1 = 5; /* qid1 should have neighbor on uz ly ux corner */ + c2 = 2; /* qid2 should have neighbor on lz uy lx corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + /* two along upper x lower y edge of coarse quad (across face to tree 1) */ + qid1 = qth_ux[0]; /* lower quad along edge on x face of coarse quad */ + qid2 = qth_ly[3]; /* upper quad along edge on y face of coarse quad */ + c1 = 4; /* qid1 should have neighbor on uz ly lx corner */ + c2 = 3; /* qid2 should have neighbor on lz uy ux corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + qid1 = qth_ux[2]; /* upper quad along edge on x face of coarse quad */ + qid2 = qth_ly[1]; /* lower quad along edge on y face of coarse quad */ + c1 = 0; /* qid1 should have neighbor on lz ly lx corner */ + c2 = 7; /* qid2 should have neighbor on uz uy ux corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + /* two along lower y upper z edge of coarse quad (across face to tree 1) */ + qid1 = qth_ly[2]; /* lower quad along edge on y face of coarse quad */ + qid2 = qth_uz[1]; /* upper quad along edge on z face of coarse quad */ + c1 = 7; /* qid1 should have neighbor on uz uy ux corner */ + c2 = 0; /* qid2 should have neighbor on lz ly lx corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + qid1 = qth_ly[3]; /* upper quad along edge on y face of coarse quad */ + qid2 = qth_uz[0]; /* lower quad along edge on z face of coarse quad */ + c1 = 6; /* qid1 should have neighbor on uz uy lx corner */ + c2 = 1; /* qid2 should have neighbor on lz ly ux corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + /*** test inter-tree corners across tree-edge ***/ + /* two pairs of corners with missing information */ + + /* two along lower x lower y edge of coarse quad (across edge to tree 0) */ + qid1 = qth_lx[0]; /* lower quad along edge on x face of coarse quad */ + qid2 = qth_ly[2]; /* upper quad along edge on y face of coarse quad */ + c1 = 5; /* qid1 should have neighbor on uz ly ux corner */ + c2 = 2; /* qid2 should have neighbor on lz uy lx corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + qid1 = qth_lx[2]; /* upper quad along edge on x face of coarse quad */ + qid2 = qth_ly[0]; /* lower quad along edge on y face of coarse quad */ + c1 = 1; /* qid1 should have neighbor on lz ly ux corner */ + c2 = 6; /* qid2 should have neighbor on uz uy lx corner */ + qtc1 = mesh->quad_to_corner[P8EST_CHILDREN * qid1 + c1]; + qtc2 = mesh->quad_to_corner[P8EST_CHILDREN * qid2 + c2]; + num_errors += check_corner_across_trees (qid1, c1, qtc1, qid2, mesh); + num_errors += check_corner_across_trees (qid2, c2, qtc2, qid1, mesh); + + /* check for errors */ + SC_CHECK_ABORT (num_errors == 0, "Missing edge-hanging corner neighbors"); + + /* cleanup */ + p8est_mesh_destroy (mesh); + p8est_ghost_destroy (ghost); + p8est_destroy (p8est); + p8est_connectivity_destroy (conn); + + sc_finalize (); + sc_MPI_Finalize (); + + return num_errors; +} diff --git a/test/test_mesh_optical2.c b/test/test_mesh_optical2.c index 7f85e63fe..ae13180a8 100644 --- a/test/test_mesh_optical2.c +++ b/test/test_mesh_optical2.c @@ -210,13 +210,13 @@ check_mesh (p4est_t * p4est, p4est_ghost_t * ghost, p4est_mesh_t * mesh, direction, neighboring_quads, neighboring_encs, neighboring_qids); -#ifdef P4EST_DEBUG +#ifdef P4EST_ENABLE_DEBUG /* print some debug info */ printf ("rank %5i, local quad %5i, global quad %5i, direction: %2i," " number of neighboring cells: %zu\n", p4est->mpirank, quad, norm_quad, direction, neighboring_quads->elem_count); -#endif /* P4EST_DEBUG */ +#endif /* P4EST_ENABLE_DEBUG */ for (j = 0; j < neighboring_quads->elem_count; ++j) { p4est_quadrant_t *q = @@ -247,13 +247,13 @@ check_mesh (p4est_t * p4est, p4est_ghost_t * ghost, p4est_mesh_t * mesh, direction, neighboring_quads, neighboring_encs, neighboring_qids); -#ifdef P4EST_DEBUG +#ifdef P4EST_ENABLE_DEBUG /* print some debug info */ printf ("rank %5i, local quad %5i, global quad %5i, direction: %2i," " number of neighboring cells: %zu\n", p4est->mpirank, quad, norm_quad, direction, neighboring_quads->elem_count); -#endif /* P4EST_DEBUG */ +#endif /* P4EST_ENABLE_DEBUG */ for (j = 0; j < neighboring_quads->elem_count; ++j) { p4est_quadrant_t *q = @@ -284,13 +284,13 @@ check_mesh (p4est_t * p4est, p4est_ghost_t * ghost, p4est_mesh_t * mesh, direction, neighboring_quads, neighboring_encs, neighboring_qids); -#ifdef P4EST_DEBUG +#ifdef P4EST_ENABLE_DEBUG /* print some debug info */ printf ("rank %5i, local quad %5i, global quad %5i, direction: %2i," " number of neighboring cells: %zu\n", p4est->mpirank, quad, norm_quad, direction, neighboring_quads->elem_count); -#endif /* P4EST_DEBUG */ +#endif /* P4EST_ENABLE_DEBUG */ for (j = 0; j < neighboring_quads->elem_count; ++j) { p4est_quadrant_t *q = diff --git a/test/test_neighbor_transform2.c b/test/test_neighbor_transform2.c new file mode 100644 index 000000000..0f11f7a04 --- /dev/null +++ b/test/test_neighbor_transform2.c @@ -0,0 +1,538 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef P4_TO_P8 +#include +#include +#else +#include +#include +#endif + +static void +quad_coords (const p4est_quadrant_t * quad, p4est_qcoord_t coords[P4EST_DIM], + int corner) +{ + p4est_qcoord_t h = P4EST_QUADRANT_LEN (quad->level); + + coords[0] = quad->x; + coords[1] = quad->y; +#ifdef P4_TO_P8 + coords[2] = quad->z; +#endif + for (int d = 0; d < P4EST_DIM; d++) { + coords[d] += (corner & (1 << d)) ? h : 0; + } +} + +#ifdef P4EST_ENABLE_DEBUG + +static int +coords_equal (const p4est_qcoord_t A[], p4est_qcoord_t B[]) +{ + for (int d = 0; d < P4EST_DIM; d++) { + if (A[d] != B[d]) { + return 0; + } + } + return 1; +} + +#endif + +static void +test_find_self_transform (sc_array_t * neigh_transforms, int t, int found[]) +{ + for (size_t iz = 0; iz < neigh_transforms->elem_count; iz++) { + p4est_neighbor_transform_t *nt = + (p4est_neighbor_transform_t *) sc_array_index (neigh_transforms, iz); + + if (found[iz]) + continue; + if (nt->neighbor_type == P4EST_CONNECT_SELF) { + P4EST_ASSERT (nt->neighbor == t); + P4EST_ASSERT (nt->index_self == 0); + P4EST_ASSERT (nt->index_neighbor == 0); + for (int d = 0; d < P4EST_DIM; d++) { + P4EST_ASSERT (nt->origin_self[d] == nt->origin_neighbor[d]); + P4EST_ASSERT (nt->perm[d] == d); + P4EST_ASSERT (nt->sign[d] == 1); + } + + for (uint64_t id = 0; id < (1 << (P4EST_DIM * 2)); id++) { + p4est_quadrant_t self_q, neigh_q; + p4est_quadrant_set_morton (&self_q, 2, id); + + /* test forward transform */ + P4EST_QUADRANT_INIT (&neigh_q); + p4est_neighbor_transform_quadrant (nt, &self_q, &neigh_q); + P4EST_ASSERT (p4est_quadrant_compare (&self_q, &neigh_q) == 0); + + /* test reverse transform */ + P4EST_QUADRANT_INIT (&self_q); + p4est_neighbor_transform_quadrant_reverse (nt, &neigh_q, &self_q); + P4EST_ASSERT (p4est_quadrant_compare (&self_q, &neigh_q) == 0); + + for (int c = 0; c < P4EST_CHILDREN; c++) { + p4est_qcoord_t self_coords[P4EST_DIM]; + p4est_qcoord_t neigh_coords[P4EST_DIM]; + + quad_coords (&self_q, self_coords, c); + + for (int d = 0; d < P4EST_DIM; d++) { + neigh_coords[d] = -1; + } + p4est_neighbor_transform_coordinates (nt, self_coords, + neigh_coords); + P4EST_ASSERT (coords_equal (self_coords, neigh_coords)); + + for (int d = 0; d < P4EST_DIM; d++) { + self_coords[d] = -1; + } + p4est_neighbor_transform_coordinates_reverse (nt, neigh_coords, + self_coords); + P4EST_ASSERT (coords_equal (self_coords, neigh_coords)); + } + } + P4EST_GLOBAL_LDEBUG ("Self transformation ok\n"); + found[iz] = 1; + return; + } + } +} + +static void +test_find_face_transform (sc_array_t * neigh_transforms, int j, int ntree, + const int ftransform[9], int found[]) +{ + for (size_t iz = 0; iz < neigh_transforms->elem_count; iz++) { + p4est_neighbor_transform_t *nt = + (p4est_neighbor_transform_t *) sc_array_index (neigh_transforms, iz); + + if (found[iz]) + continue; + if (nt->neighbor_type == P4EST_CONNECT_FACE && nt->index_self == j) { + P4EST_ASSERT (nt->neighbor == ntree); + P4EST_ASSERT (0 <= nt->index_neighbor + && nt->index_neighbor < P4EST_FACES); + + for (uint64_t id = 0; id < (1 << (P4EST_DIM * 2)); id++) { + p4est_quadrant_t self_quad[2]; + int fs[2]; + + fs[0] = j; + fs[1] = j ^ 1; + + /* only test quadrants that touch face j */ + p4est_quadrant_set_morton (&self_quad[0], 2, id); + p4est_quadrant_face_neighbor (&self_quad[0], j, &self_quad[1]); + if (p4est_quadrant_is_inside_root (&self_quad[1])) { + continue; + } + + for (int s = 0; s < 2; s++) { + p4est_quadrant_t neigh_quad, neigh_quad_f, self_quad_rev; + + /* compare neighbor transform and face transform */ + p4est_quadrant_transform_face (&self_quad[s], &neigh_quad_f, + ftransform); + p4est_neighbor_transform_quadrant (nt, &self_quad[s], &neigh_quad); + P4EST_ASSERT (p4est_quadrant_compare (&neigh_quad, &neigh_quad_f) == + 0); + + /* reverse is a left inverse */ + p4est_neighbor_transform_quadrant_reverse (nt, &neigh_quad, + &self_quad_rev); + P4EST_ASSERT (p4est_quadrant_compare (&self_quad[s], &self_quad_rev) + == 0); + + for (int fc = 0; fc < P4EST_CHILDREN / 2; fc++) { + p4est_qcoord_t self_coords[P4EST_DIM]; + p4est_qcoord_t neigh_coords[P4EST_DIM]; + p4est_qcoord_t neigh_coords_f[P4EST_DIM]; + p4est_qcoord_t self_coords_rev[P4EST_DIM]; + int c = p4est_face_corners[fs[s]][fc]; + + quad_coords (&self_quad[s], self_coords, c); + p4est_coordinates_transform_face (self_coords, neigh_coords_f, + ftransform); + p4est_neighbor_transform_coordinates (nt, self_coords, + neigh_coords); + P4EST_ASSERT (coords_equal (neigh_coords_f, neigh_coords)); + + p4est_neighbor_transform_coordinates_reverse (nt, neigh_coords, + self_coords_rev); + P4EST_ASSERT (coords_equal (self_coords, self_coords_rev)); + } + } + + } + P4EST_GLOBAL_LDEBUGF ("Face transformation across %d ok\n", j); + found[iz] = 1; + return; + } + } +} + +static void +test_self_transform (p4est_connectivity_t * conn, p4est_topidx_t t, int j, + sc_array_t * neigh_transforms) +{ + int found = 0; + + P4EST_ASSERT (neigh_transforms->elem_count == 1); + test_find_self_transform (neigh_transforms, t, &found); + P4EST_ASSERT (found); +} + +static void +test_face_transform (p4est_connectivity_t * conn, p4est_topidx_t t, int j, + sc_array_t * neigh_transforms) +{ + int *found = + P4EST_ALLOC_ZERO (int, neigh_transforms->elem_count); + p4est_topidx_t ntree; + int ftransform[9]; + + P4EST_ASSERT (neigh_transforms->elem_count == 1 + || neigh_transforms->elem_count == 2); + test_find_self_transform (neigh_transforms, t, found); + ntree = p4est_find_face_transform (conn, t, j, ftransform); + if (ntree >= 0) { + test_find_face_transform (neigh_transforms, j, ntree, ftransform, found); + } + for (size_t iz = 0; iz < neigh_transforms->elem_count; iz++) { + P4EST_ASSERT (found[iz]); + } + P4EST_FREE (found); +} + +#ifdef P4_TO_P8 +static void +test_find_edge_transform (sc_array_t * neigh_transforms, p4est_topidx_t t, + int j, p8est_edge_info_t * ei, + p8est_edge_transform_t * et, int found[]) +{ + for (size_t iz = 0; iz < neigh_transforms->elem_count; iz++) { + p4est_neighbor_transform_t *nt = + (p4est_neighbor_transform_t *) sc_array_index (neigh_transforms, iz); + + if (found[iz]) + continue; + if (nt->neighbor_type == P8EST_CONNECT_EDGE && nt->index_self == j) { + P4EST_ASSERT (nt->neighbor == et->ntree); + P4EST_ASSERT (nt->index_neighbor == et->nedge); + + for (uint64_t id = 0; id < (1 << (P4EST_DIM * 2)); id++) { + p4est_quadrant_t self_quad[2]; + int es[2]; + + es[0] = j; + es[1] = j ^ 3; + + /* only test quadrants that touch face j */ + p4est_quadrant_set_morton (&self_quad[0], 2, id); + p8est_quadrant_edge_neighbor (&self_quad[0], j, &self_quad[1]); + if (!p8est_quadrant_is_outside_edge (&self_quad[1])) { + continue; + } + + for (int s = 0; s < 2; s++) { + p4est_quadrant_t neigh_quad, neigh_quad_e, self_quad_rev; + + /* compare neighbor transform and face transform */ + p8est_quadrant_transform_edge (&self_quad[s], &neigh_quad_e, ei, et, + s); + p4est_neighbor_transform_quadrant (nt, &self_quad[s], &neigh_quad); + P4EST_ASSERT (p4est_quadrant_compare (&neigh_quad, &neigh_quad_e) == + 0); + + /* reverse is a left inverse */ + p4est_neighbor_transform_quadrant_reverse (nt, &neigh_quad, + &self_quad_rev); + P4EST_ASSERT (p4est_quadrant_compare (&self_quad[s], &self_quad_rev) + == 0); + + for (int ec = 0; ec < 2; ec++) { + p4est_qcoord_t self_coords[P4EST_DIM]; + p4est_qcoord_t neigh_coords[P4EST_DIM]; + p4est_qcoord_t neigh_coords_e[P4EST_DIM]; + p4est_qcoord_t self_coords_rev[P4EST_DIM]; + int c = p8est_edge_corners[es[s]][ec]; + + quad_coords (&self_quad[s], self_coords, c); + p8est_coordinates_transform_edge (self_coords, neigh_coords_e, ei, + et); + p4est_neighbor_transform_coordinates (nt, self_coords, + neigh_coords); + P4EST_ASSERT (coords_equal (neigh_coords_e, neigh_coords)); + + p4est_neighbor_transform_coordinates_reverse (nt, neigh_coords, + self_coords_rev); + P4EST_ASSERT (coords_equal (self_coords, self_coords_rev)); + } + } + + } + P4EST_GLOBAL_LDEBUGF ("Edge transformation across %d to %d ok\n", j, + et->ntree); + found[iz] = 1; + return; + } + } +} + +static void +test_find_all_edge_transforms (p4est_connectivity_t * conn, p4est_topidx_t t, + int j, sc_array_t * neigh_transforms, + int found[]) +{ + p8est_edge_info_t ei; + sc_array_t *eta = &ei.edge_transforms; + + sc_array_init (eta, sizeof (p8est_edge_transform_t)); + p8est_find_edge_transform (conn, t, j, &ei); + for (size_t iz = 0; iz < eta->elem_count; iz++) { + p8est_edge_transform_t *et = + (p8est_edge_transform_t *) sc_array_index (eta, iz); + + test_find_edge_transform (neigh_transforms, t, j, &ei, et, found); + } + sc_array_reset (eta); +} + +static void +test_edge_transform (p4est_connectivity_t * conn, p4est_topidx_t t, int j, + sc_array_t * neigh_transforms) +{ + int *found = + P4EST_ALLOC_ZERO (int, neigh_transforms->elem_count); + + P4EST_ASSERT (neigh_transforms->elem_count >= 1); + test_find_self_transform (neigh_transforms, t, found); + for (int ef = 0; ef < 2; ef++) { + int f = p8est_edge_faces[j][ef]; + int ftransform[9]; + p4est_topidx_t ntree; + + ntree = p4est_find_face_transform (conn, t, f, ftransform); + if (ntree >= 0) { + test_find_face_transform (neigh_transforms, f, ntree, ftransform, + found); + } + } + test_find_all_edge_transforms (conn, t, j, neigh_transforms, found); + for (size_t iz = 0; iz < neigh_transforms->elem_count; iz++) { + P4EST_ASSERT (found[iz]); + } + P4EST_FREE (found); +} +#endif + +static void +test_find_corner_transform (sc_array_t * neigh_transforms, p4est_topidx_t t, + int j, p4est_corner_transform_t * ct, int found[]) +{ + for (size_t iz = 0; iz < neigh_transforms->elem_count; iz++) { + p4est_neighbor_transform_t *nt = + (p4est_neighbor_transform_t *) sc_array_index (neigh_transforms, iz); + + if (found[iz]) + continue; + if (nt->neighbor_type == P4EST_CONNECT_CORNER && nt->index_self == j) { + p4est_quadrant_t root, self_quad[2]; + int cs[2], ncs[2]; + + cs[0] = j; + cs[1] = j ^ (P4EST_CHILDREN - 1); + ncs[0] = (ct->ncorner) ^ (P4EST_CHILDREN - 1); + ncs[1] = ct->ncorner; + p4est_qcoord_t self_coords[P4EST_DIM]; + p4est_qcoord_t neigh_coords_c[P4EST_DIM]; + p4est_qcoord_t neigh_coords[P4EST_DIM]; + p4est_qcoord_t self_coords_rev[P4EST_DIM]; + + P4EST_ASSERT (nt->neighbor == ct->ntree); + P4EST_ASSERT (nt->index_neighbor == ct->ncorner); + + memset (&root, 0, sizeof (root)); + p4est_quadrant_corner_descendant (&root, &self_quad[0], j, 2); + p4est_quadrant_corner_neighbor (&self_quad[0], j, &self_quad[1]); + for (int s = 0; s < 2; s++) { + p4est_quadrant_t neigh_quad_c, neigh_quad, self_quad_rev; + + neigh_quad_c = self_quad[s]; + p4est_quadrant_transform_corner (&neigh_quad_c, ct->ncorner, s); + p4est_neighbor_transform_quadrant (nt, &self_quad[s], &neigh_quad); + P4EST_ASSERT (p4est_quadrant_compare (&neigh_quad_c, &neigh_quad) == + 0); + + p4est_neighbor_transform_quadrant_reverse (nt, &neigh_quad, + &self_quad_rev); + P4EST_ASSERT (p4est_quadrant_compare (&self_quad[s], &self_quad_rev) + == 0); + + quad_coords (&self_quad[s], self_coords, cs[s]); + quad_coords (&neigh_quad, neigh_coords_c, ncs[s]); + p4est_neighbor_transform_coordinates (nt, self_coords, neigh_coords); + P4EST_ASSERT (coords_equal (neigh_coords_c, neigh_coords)); + + p4est_neighbor_transform_coordinates_reverse (nt, neigh_coords, + self_coords_rev); + P4EST_ASSERT (coords_equal (self_coords, self_coords_rev)); + } + P4EST_GLOBAL_LDEBUGF ("Corner transformation across %d to %d ok\n", j, + ct->ntree); + found[iz] = 1; + return; + } + } +} + +static void +test_corner_transform (p4est_connectivity_t * conn, p4est_topidx_t t, int j, + sc_array_t * neigh_transforms) +{ + int *found = + P4EST_ALLOC_ZERO (int, neigh_transforms->elem_count); + p4est_corner_info_t ci; + sc_array_t *cta = &ci.corner_transforms;; + + P4EST_ASSERT (neigh_transforms->elem_count >= 1); + test_find_self_transform (neigh_transforms, t, found); + for (int cf = 0; cf < P4EST_DIM; cf++) { + int f = p4est_corner_faces[j][cf]; + int ftransform[9]; + p4est_topidx_t ntree; + + ntree = p4est_find_face_transform (conn, t, f, ftransform); + if (ntree >= 0) { + test_find_face_transform (neigh_transforms, f, ntree, ftransform, + found); + } + } +#ifdef P4_TO_P8 + for (int ce = 0; ce < P4EST_DIM; ce++) { + int e = p8est_corner_edges[j][ce]; + + test_find_all_edge_transforms (conn, t, e, neigh_transforms, found); + } +#endif + sc_array_init (cta, sizeof (p4est_corner_transform_t)); + p4est_find_corner_transform (conn, t, j, &ci); + for (size_t iz = 0; iz < cta->elem_count; iz++) { + p4est_corner_transform_t *ct = + (p4est_corner_transform_t *) sc_array_index (cta, iz); + + test_find_corner_transform (neigh_transforms, t, j, ct, found); + } + sc_array_reset (cta); + for (size_t iz = 0; iz < neigh_transforms->elem_count; iz++) { + P4EST_ASSERT (found[iz]); + } + P4EST_FREE (found); +} + +int +main (int argc, char **argv) +{ + sc_MPI_Init (&argc, &argv); + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_DEBUG); + p4est_init (NULL, SC_LP_DEBUG); +#ifndef P4_TO_P8 +#define N_NAMES 13 + const char *names[N_NAMES] = + { "brick23", "corner", "cubed", "disk", "icosahedron", "moebius", + "periodic", "pillow", "rotwrap", "star", "shell2d", "disk2d", "unit" + }; + const p4est_connect_type_t neigh_types[3] = + { P4EST_CONNECT_SELF, P4EST_CONNECT_FACE, P4EST_CONNECT_CORNER }; + const char *neigh_type_names[3] = { "self", "face", "corner" }; + const int neigh_sizes[3] = { 1, P4EST_FACES, P4EST_CHILDREN }; +#else +#define N_NAMES 9 + const char *names[] = + { "brick235", "periodic", "rotcubes", "rotwrap", "shell", "sphere", + "twocubes", "twowrap", "unit" + }; + const p4est_connect_type_t neigh_types[4] = + { P4EST_CONNECT_SELF, P4EST_CONNECT_FACE, P8EST_CONNECT_EDGE, + P4EST_CONNECT_CORNER + }; + const char *neigh_type_names[4] = + { "self", "face", "edge", "corner" }; + const int neigh_sizes[4] = + { 1, P4EST_FACES, P8EST_EDGES, P4EST_CHILDREN }; +#endif + + for (int i = 0; i < N_NAMES; i++) { + p4est_connectivity_t *conn = p4est_connectivity_new_byname (names[i]); + P4EST_ASSERT (conn != NULL); + + for (p4est_topidx_t t = 0; t < conn->num_trees; t++) { + for (int n = 0; n <= P4EST_DIM; n++) { + p4est_connect_type_t neigh_type = neigh_types[n]; + + for (int j = 0; j < neigh_sizes[n]; j++) { + sc_array_t neigh_transforms; + + P4EST_GLOBAL_LDEBUGF ("Testing connectivity %s, tree %d, %s %d\n", + names[i], t, neigh_type_names[n], j); + p4est_log_indent_push (); + + sc_array_init (&neigh_transforms, + sizeof (p4est_neighbor_transform_t)); + p4est_connectivity_get_neighbor_transforms (conn, t, neigh_type, j, + &neigh_transforms); + + switch (n) { + case 0: + test_self_transform (conn, t, j, &neigh_transforms); + break; + case 1: + test_face_transform (conn, t, j, &neigh_transforms); + break; +#ifdef P4_TO_P8 + case 2: + test_edge_transform (conn, t, j, &neigh_transforms); + break; +#endif + case P4EST_DIM: + test_corner_transform (conn, t, j, &neigh_transforms); + break; + default: + SC_ABORT_NOT_REACHED (); + } + + sc_array_reset (&neigh_transforms); + p4est_log_indent_pop (); + } + } + } + + p4est_connectivity_destroy (conn); + } + sc_MPI_Finalize (); + return 0; +} diff --git a/test/test_neighbor_transform3.c b/test/test_neighbor_transform3.c new file mode 100644 index 000000000..f24408537 --- /dev/null +++ b/test/test_neighbor_transform3.c @@ -0,0 +1,26 @@ +/* + This file is part of p4est. + p4est is a C library to manage a collection (a forest) of multiple + connected adaptive quadtrees or octrees in parallel. + + Copyright (C) 2010 The University of Texas System + Additional copyright (C) 2011 individual authors + Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac + + p4est 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. + + p4est 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 p4est; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include "test_neighbor_transform2.c" diff --git a/test/test_partition2.c b/test/test_partition2.c index 994d92026..db825ed9a 100644 --- a/test/test_partition2.c +++ b/test/test_partition2.c @@ -50,6 +50,9 @@ init_fn (p4est_t * p4est, p4est_topidx_t which_tree, { user_data_t *data = (user_data_t *) quadrant->p.user_data; + /* prevent uninitialized bytes due to compiler padding */ + memset (data, -1, sizeof (user_data_t)); + data->a = which_tree; data->sum = quadrant->x + quadrant->y + quadrant->level; } @@ -167,7 +170,7 @@ test_transfer_post (test_transfer_t * tt, p4est_t * p4est) back = tt->back; P4EST_ASSERT (p4est->data_size == back->data_size); - /* now back is a copy of the p4est before partiton */ + /* now back is a copy of the p4est before partition */ /* p4est has been partitioned once */ /* put together some buffers */ @@ -308,10 +311,17 @@ test_pertree (p4est_t * p4est, const p4est_gloidx_t * prev_pertree, p4est_search_partition (p4est, 1, traverse_fn, NULL, NULL); } +static unsigned +test_checksum (p4est_t * p4est, int have_zlib) +{ + return have_zlib ? p4est_checksum (p4est) : 0; +} + static void test_partition_circle (sc_MPI_Comm mpicomm, p4est_connectivity_t * connectivity, - p4est_gloidx_t * pertree1, p4est_gloidx_t * pertree2) + p4est_gloidx_t * pertree1, p4est_gloidx_t * pertree2, + int have_zlib) { int i, j; int num_procs; @@ -331,9 +341,9 @@ test_partition_circle (sc_MPI_Comm mpicomm, test_pertree (p4est, NULL, pertree1); global_num = p4est->global_num_quadrants; - crc1 = p4est_checksum (p4est); + crc1 = test_checksum (p4est, have_zlib); copy = p4est_copy (p4est, 1); - P4EST_ASSERT (p4est_checksum (copy) == crc1); + P4EST_ASSERT (test_checksum (copy, have_zlib) == crc1); new_counts = P4EST_ALLOC (p4est_locidx_t, num_procs); @@ -359,7 +369,7 @@ test_partition_circle (sc_MPI_Comm mpicomm, p4est_partition_given (p4est, new_counts); test_transfer_post (tt, p4est); test_pertree (p4est, pertree1, pertree2); - crc2 = p4est_checksum (p4est); + crc2 = test_checksum (p4est, have_zlib); SC_CHECK_ABORT (crc1 == crc2, "First checksum mismatch"); } @@ -386,7 +396,7 @@ test_partition_circle (sc_MPI_Comm mpicomm, p4est_partition_given (p4est, new_counts); test_transfer_post (tt, p4est); test_pertree (p4est, pertree1, pertree2); - crc2 = p4est_checksum (p4est); + crc2 = test_checksum (p4est, have_zlib); SC_CHECK_ABORT (crc1 == crc2, "Second checksum mismatch"); } @@ -396,7 +406,7 @@ test_partition_circle (sc_MPI_Comm mpicomm, p4est_partition (p4est, 0, NULL); test_transfer_post (tt, p4est); test_pertree (p4est, pertree1, pertree2); - crc2 = p4est_checksum (p4est); + crc2 = test_checksum (p4est, have_zlib); SC_CHECK_ABORT (crc1 == crc2, "Third checksum mismatch"); SC_CHECK_ABORT (p4est_is_equal (p4est, copy, 1), "Forest mismatch"); @@ -411,6 +421,7 @@ main (int argc, char **argv) int rank; int num_procs; int mpiret; + int have_zlib; sc_MPI_Comm mpicomm; p4est_t *p4est, *copy; p4est_connectivity_t *connectivity; @@ -433,7 +444,15 @@ main (int argc, char **argv) mpiret = sc_MPI_Comm_rank (mpicomm, &rank); SC_CHECK_MPI (mpiret); + /* establish parallel logging */ sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT); + p4est_init (NULL, SC_LP_DEFAULT); + + /* check for ZLIB usability */ + if (!(have_zlib = p4est_have_zlib ())) { + P4EST_GLOBAL_LERROR + ("Not found a working ZLIB installation: ignoring CRCs\n"); + } /* create connectivity and forest structures */ #ifdef P4_TO_P8 @@ -469,7 +488,7 @@ main (int argc, char **argv) "Negative number of quadrants on the last processor"); /* Save a checksum of the original forest */ - crc = p4est_checksum (p4est); + crc = test_checksum (p4est, have_zlib); /* partition the forest */ tt = test_transfer_pre (p4est); @@ -478,7 +497,7 @@ main (int argc, char **argv) test_pertree (p4est, pertree1, pertree2); /* Double check that we didn't loose any quads */ - SC_CHECK_ABORT (crc == p4est_checksum (p4est), + SC_CHECK_ABORT (crc == test_checksum (p4est, have_zlib), "bad checksum, missing a quad"); /* count the actual number of quadrants per proc */ @@ -504,12 +523,13 @@ main (int argc, char **argv) p4est_partition (p4est, 0, weight_one); test_transfer_post (tt, p4est); test_pertree (p4est, pertree1, pertree2); - SC_CHECK_ABORT (crc == p4est_checksum (p4est), + SC_CHECK_ABORT (crc == test_checksum (p4est, have_zlib), "bad checksum after uniformly weighted partition"); /* copy the p4est */ copy = p4est_copy (p4est, 1); - SC_CHECK_ABORT (crc == p4est_checksum (copy), "bad checksum after copy"); + SC_CHECK_ABORT (crc == test_checksum (copy, have_zlib), + "bad checksum after copy"); /* do a weighted partition with many zero weights */ weight_counter = 0; @@ -518,7 +538,7 @@ main (int argc, char **argv) p4est_partition (copy, 0, weight_once); test_transfer_post (tt, copy); test_pertree (copy, pertree1, pertree2); - SC_CHECK_ABORT (crc == p4est_checksum (copy), + SC_CHECK_ABORT (crc == test_checksum (copy, have_zlib), "bad checksum after unevenly weighted partition 1"); /* do a weighted partition with many zero weights */ @@ -528,7 +548,7 @@ main (int argc, char **argv) p4est_partition (copy, 0, weight_once); test_transfer_post (tt, copy); test_pertree (copy, pertree1, pertree2); - SC_CHECK_ABORT (crc == p4est_checksum (copy), + SC_CHECK_ABORT (crc == test_checksum (copy, have_zlib), "bad checksum after unevenly weighted partition 2"); /* do a weighted partition with many zero weights @@ -543,7 +563,7 @@ main (int argc, char **argv) p4est_partition (copy, 0, weight_once); test_transfer_post (tt, copy); test_pertree (copy, pertree1, pertree2); - SC_CHECK_ABORT (crc == p4est_checksum (copy), + SC_CHECK_ABORT (crc == test_checksum (copy, have_zlib), "bad checksum after unevenly weighted partition 3"); /* check user data content */ @@ -560,7 +580,8 @@ main (int argc, char **argv) } /* Add another test. Overwrites pertree1, pertree2 */ - test_partition_circle (mpicomm, connectivity, pertree1, pertree2); + test_partition_circle (mpicomm, connectivity, pertree1, pertree2, + have_zlib); /* clean up and exit */ P4EST_FREE (pertree1); diff --git a/test/test_plex2.c b/test/test_plex2.c index 8ae01ba78..c7412100c 100644 --- a/test/test_plex2.c +++ b/test/test_plex2.c @@ -162,7 +162,7 @@ static int refine_tree_one_fn (p4est_t * p4est, p4est_topidx_t which_tree, p4est_quadrant_t * quadrant) { - return ! !which_tree; + return !!which_tree; } static int @@ -371,6 +371,43 @@ test_small (int argc, char **argv) return 0; } +static int +test_periodic (int argc, char **argv) +{ + sc_MPI_Comm mpicomm; + int mpiret; + p4est_t *p4est; + p4est_connectivity_t *conn; + + /* initialize MPI */ + mpicomm = sc_MPI_COMM_WORLD; + +#ifndef P4_TO_P8 + conn = p4est_connectivity_new_brick (3, 3, 1, 1); +#else + conn = p8est_connectivity_new_brick (3, 3, 3, 1, 1, 1); +#endif + p4est = p4est_new (mpicomm, conn, 0, NULL, NULL); + p4est_refine (p4est, 0, refine_tree_one_fn, NULL); + + mpiret = test_forest (argc, argv, p4est, 0); + if (mpiret) { + return mpiret; + } + + p4est_partition (p4est, 0, NULL); + + mpiret = test_forest (argc, argv, p4est, 0); + if (mpiret) { + return mpiret; + } + + p4est_destroy (p4est); + p4est_connectivity_destroy (conn); + + return 0; +} + int main (int argc, char **argv) { @@ -389,6 +426,10 @@ main (int argc, char **argv) if (mpiret) { return mpiret; } + mpiret = test_periodic (argc, argv); + if (mpiret) { + return mpiret; + } mpiret = test_big (argc, argv); if (mpiret) { return mpiret; diff --git a/test/test_valid2.c b/test/test_valid2.c index 313e2dcc8..44fb77256 100644 --- a/test/test_valid2.c +++ b/test/test_valid2.c @@ -101,6 +101,7 @@ check_all (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn, unsigned crc_partition_expected, unsigned gcrc_expected) { int mpiret; + int have_zlib; unsigned crc_computed, crc_partition_computed, gcrc_computed; long long lsize[3], gsize[3]; size_t size_conn, size_p4est, size_ghost; @@ -110,6 +111,13 @@ check_all (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn, P4EST_GLOBAL_STATISTICSF ("Testing configuration %s\n", vtkname); + /* check for ZLIB usability */ + if (!(have_zlib = p4est_have_zlib ())) { + P4EST_GLOBAL_LERROR + ("Not found a working ZLIB installation: ignoring CRCs\n"); + crc_expected = crc_partition_expected = gcrc_expected = 0; + } + p4est = p4est_new_ext (mpicomm, conn, 0, 0, 0, 0, NULL, NULL); p4est_refine (p4est, 1, refine_fn, NULL); p4est_coarsen (p4est, 1, coarsen_fn, NULL); @@ -117,8 +125,8 @@ check_all (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn, p4est_partition (p4est, 0, NULL); p4est_vtk_write_file (p4est, NULL, vtkname); - crc_computed = p4est_checksum (p4est); - crc_partition_computed = p4est_checksum_partition (p4est); + crc_computed = have_zlib ? p4est_checksum (p4est) : 0; + crc_partition_computed = have_zlib ? p4est_checksum_partition (p4est) : 0; P4EST_GLOBAL_STATISTICSF ("Forest checksum 0x%08x\n", crc_computed); P4EST_GLOBAL_STATISTICSF ("Forest partition checksum 0x%08x\n", crc_partition_computed); @@ -137,13 +145,14 @@ check_all (sc_MPI_Comm mpicomm, p4est_connectivity_t * conn, lsize[0] = (long long) size_conn; lsize[1] = (long long) size_p4est; lsize[2] = (long long) size_ghost; + gsize[0] = gsize[1] = gsize[2] = 0; mpiret = sc_MPI_Reduce (lsize, gsize, 3, sc_MPI_LONG_LONG_INT, sc_MPI_SUM, 0, mpicomm); SC_CHECK_MPI (mpiret); P4EST_GLOBAL_INFOF ("Global byte sizes: %lld %lld %lld\n", gsize[0], gsize[1], gsize[2]); - gcrc_computed = p4est_ghost_checksum (p4est, ghost); + gcrc_computed = have_zlib ? p4est_ghost_checksum (p4est, ghost) : 0; P4EST_GLOBAL_STATISTICSF ("Ghost checksum 0x%08x\n", gcrc_computed); if (p4est->mpisize == 2 && p4est->mpirank == 0) { SC_CHECK_ABORT (gcrc_computed == gcrc_expected, @@ -198,9 +207,11 @@ main (int argc, char **argv) mpiret = sc_MPI_Comm_rank (mpicomm, &rank); SC_CHECK_MPI (mpiret); + /* establish parallel logging */ sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT); p4est_init (NULL, SC_LP_DEFAULT); + /* balance can optionally be across edges and corners, too */ (void) check_backward_compatibility (); check_int_types (); diff --git a/test/test_version.c b/test/test_version.c index 9c1bd88dd..86e582af8 100644 --- a/test/test_version.c +++ b/test/test_version.c @@ -40,8 +40,10 @@ main (int argc, char **argv) SC_CHECK_MPI (mpiret); mpicomm = sc_MPI_COMM_WORLD; + SC_CHECK_ABORT (!p4est_is_initialized (), "Verify pre-initialization"); sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT); p4est_init (NULL, SC_LP_DEFAULT); + SC_CHECK_ABORT (p4est_is_initialized (), "Verify initialization"); /* check all functions related to version numbers of p4est */ num_failed_tests = 0;