├── .clang-format ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── CajitaConfig.cmake.in ├── CajitaSettings.cmake.in ├── FindCLANG_FORMAT.cmake ├── FindHYPRE.cmake └── SetupVersion.cmake ├── src ├── CMakeLists.txt ├── Cajita.hpp ├── Cajita_Array.hpp ├── Cajita_BovWriter.hpp ├── Cajita_Config.hpp.in ├── Cajita_FastFourierTransform.hpp ├── Cajita_GlobalGrid.cpp ├── Cajita_GlobalGrid.hpp ├── Cajita_GlobalMesh.cpp ├── Cajita_GlobalMesh.hpp ├── Cajita_Halo.hpp ├── Cajita_HypreStructuredSolver.hpp ├── Cajita_IndexSpace.cpp ├── Cajita_IndexSpace.hpp ├── Cajita_Interpolation.hpp ├── Cajita_LocalGrid.cpp ├── Cajita_LocalGrid.hpp ├── Cajita_LocalMesh.hpp ├── Cajita_ManualPartitioner.cpp ├── Cajita_ManualPartitioner.hpp ├── Cajita_MpiTraits.hpp ├── Cajita_Partitioner.hpp ├── Cajita_ReferenceStructuredSolver.hpp ├── Cajita_Splines.hpp ├── Cajita_Types.hpp ├── Cajita_UniformDimPartitioner.cpp ├── Cajita_UniformDimPartitioner.hpp └── Cajita_Version.hpp.in └── unit_test ├── CMakeLists.txt ├── TestCUDA_Category.hpp ├── TestOPENMP_Category.hpp ├── TestSERIAL_Category.hpp ├── tstArray.hpp ├── tstBovWriter.hpp ├── tstFastFourierTransform.hpp ├── tstGlobalGrid.hpp ├── tstGlobalMesh.hpp ├── tstHalo.hpp ├── tstHypreStructuredSolver.hpp ├── tstIndexSpace.hpp ├── tstInterpolation.hpp ├── tstLocalGrid.hpp ├── tstLocalMesh.hpp ├── tstSplineEvaluation.hpp ├── tstSplines.hpp └── unit_test_main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | --- 3 | Language: Cpp 4 | AlwaysBreakTemplateDeclarations: true 5 | BreakBeforeBraces: Allman 6 | BinPackParameters: true 7 | IndentWidth: 4 8 | SpacesInParentheses: true 9 | BreakConstructorInitializersBeforeComma: true 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | # against hwloc error (travis-ci/travis-ci#10019) 4 | sudo: required 5 | 6 | stages: 7 | - canary 8 | - test 9 | 10 | jobs: 11 | include: 12 | - stage: canary 13 | env: BACKENDS="OpenMP" WERROR=ON 14 | 15 | env: 16 | global: 17 | - CCACHE_CPP2=yes # for clang 18 | matrix: # note this matrix only applies to the "test" stage 19 | - BACKENDS="OPENMP" WERROR=ON CMAKE_BUILD_TYPE=Release 20 | - BACKENDS="SERIAL" WERROR=ON CMAKE_BUILD_TYPE=Release 21 | - BACKENDS="SERIAL OpenMP" WERROR=ON CMAKE_BUILD_TYPE=Release 22 | - BACKENDS="OPENMP" COVERAGE=ON HYPRE=ON CMAKE_BUILD_TYPE=Debug 23 | - BACKENDS="SERIAL" COVERAGE=ON HYPRE=ON CMAKE_BUILD_TYPE=Debug 24 | 25 | before_script: 26 | - sudo ln -s /usr/bin/ccache /usr/lib/ccache/clang++ 27 | - ccache -z 28 | - KOKKOS_OPTS=( --with-hwloc=/usr --gcc-toolchain=/usr ) 29 | - for i in ${BACKENDS}; do KOKKOS_OPTS+=( --with-${i,,[A-Z]} ); done 30 | # LD_LIBRARY_PATH workaround for libomp: https://github.com/travis-ci/travis-ci/issues/8613 31 | - if [[ ${CC} = clang ]]; then export LD_LIBRARY_PATH=/usr/local/clang/lib:$LD_LIBRARY_PATH; export OMPI_CXX=clang++; export OMPI_CC=clang; export OMPI_FC=gfortran-6; export CXX=mpicxx; export CC=mpicc; export FC=mpif90; fi 32 | - if [[ ${CC} = gcc ]]; then export OMPI_CXX=g++-6; export OMPI_CC=gcc-6; export OMPI_FC=gfortran-6; export CXX=mpicxx; export CC=mpicc; export FC=mpif90; fi 33 | - git clone --depth=1 https://github.com/kokkos/kokkos.git && 34 | pushd kokkos && 35 | mkdir build && 36 | pushd build && 37 | cmake -DCMAKE_INSTALL_PREFIX=$HOME/kokkos ${KOKKOS_OPTS[@]} .. && 38 | make -j2 && 39 | make install && 40 | popd && 41 | popd 42 | - if [[ ${HYPRE} ]]; then 43 | git clone --depth=1 https://github.com/hypre-space/hypre.git && 44 | pushd hypre && 45 | git fetch --all --tags && 46 | git checkout v2.18.2 && 47 | pushd src && 48 | ./configure --prefix=$HOME/hypre && 49 | make -j2 && 50 | make install && 51 | popd && 52 | popd; 53 | fi 54 | - if [[ ${HEFFTE} ]]; then 55 | git clone --depth=1 https://bitbucket.org/icl/heffte.git && 56 | pushd heffte && 57 | mkdir build && 58 | pushd build && 59 | cmake -DCMAKE_INSTALL_PREFIX="$HOME/heffte" -DFFTW_USE_STATIC_LIBS=ON -DBUILD_GPU=OFF .. && 60 | make -j2 VERBOSE=1&& 61 | make install && 62 | popd && 63 | popd; 64 | fi 65 | - if [[ ${COVERAGE} ]]; then 66 | pip install --user coverxygen && 67 | mkdir -p $HOME/.local/bin && wget -O $HOME/.local/bin/codecov https://codecov.io/bash && chmod +x $HOME/.local/bin/codecov; 68 | fi 69 | 70 | addons: 71 | apt: 72 | sources: 73 | - ubuntu-toolchain-r-test 74 | packages: 75 | - libhwloc-dev 76 | - openmpi-bin 77 | - libopenmpi-dev 78 | - libgsl0-dev 79 | - python-pip 80 | - g++-6 81 | - gfortran-6 82 | - libfftw3-dev 83 | 84 | script: 85 | - export CXXFLAGS="-Wall -Wextra -pedantic ${WERROR:+-Werror}" 86 | - export FFLAGS="-Wall -Wextra -pedantic ${WERROR:+-Werror}" 87 | - mkdir build && pushd build && 88 | cmake -DCMAKE_PREFIX_PATH="$HOME/kokkos;$HOME/hypre;$HOME/heffte" 89 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache 90 | ${CMAKE_OPTS[@]} 91 | -DCajita_ENABLE_TESTING=ON 92 | ${CMAKE_BUILD_TYPE:+-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}} 93 | ${COVERAGE:+-DCajita_ENABLE_COVERAGE_BUILD=ON} .. && 94 | make -k -j2 VERBOSE=1 && 95 | make test CTEST_OUTPUT_ON_FAILURE=1 && 96 | make format && git diff --exit-code && 97 | make install DESTDIR=${PWD}/install && rm -rf ${PWD}/install/usr/local && rmdir ${PWD}/install/usr && 98 | popd 99 | 100 | after_success: 101 | - ccache -s 102 | - test "${TRAVIS_BUILD_STAGE_NAME}" = "Test" || travis_terminate 0 103 | - if [[ ${COVERAGE} ]]; then 104 | pushd build && 105 | if [[ ${CC} = clang ]]; then 106 | codecov -x "llvm-cov gcov" -F "${CC}"; 107 | else 108 | codecov -x gcov-6 -F "${CC}"; 109 | fi && 110 | popd; 111 | fi 112 | - if [[ ${TRAVIS_JOB_NUMBER} = *.2 ]]; then 113 | git fetch origin gh-pages && git checkout -b gh-pages FETCH_HEAD && 114 | if [[ ${TRAVIS_BRANCH} = master && ${encrypted_de2ca53a1b69_key} && ${encrypted_de2ca53a1b69_iv} && ${TRAVIS_PULL_REQUEST} == false ]]; then 115 | git config --global user.name "Automatic Deployment (Travis CI)"; 116 | git config --global user.email "noreply@ornl.gov"; 117 | git commit -m "Documentation Update"; 118 | openssl aes-256-cbc -K $encrypted_de2ca53a1b69_key -iv $encrypted_de2ca53a1b69_iv -in deploy.enc -out ~/.ssh/id_rsa -d; 119 | chmod 600 ~/.ssh/id_rsa; 120 | git push git@github.com:${TRAVIS_REPO_SLUG} gh-pages:gh-pages; 121 | else 122 | git status; 123 | git diff --cached --no-color | head -n 500; 124 | fi; 125 | fi 126 | 127 | branches: 128 | only: 129 | - master 130 | 131 | cache: 132 | - ccache 133 | 134 | compiler: 135 | - gcc 136 | - clang 137 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # project settings 2 | cmake_minimum_required(VERSION 3.12) 3 | 4 | project(Cajita LANGUAGES CXX VERSION 0.1.0) 5 | 6 | # Download and unpack googletest at configure time 7 | include(FetchContent) 8 | FetchContent_Declare( 9 | googletest 10 | URL https://github.com/google/googletest/archive/release-1.10.0.tar.gz 11 | ) 12 | FetchContent_GetProperties(googletest) 13 | if(NOT googletest_POPULATED) 14 | FetchContent_Populate(googletest) 15 | add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) 16 | endif() 17 | 18 | # Prevent GoogleTest from overriding our compiler/linker options 19 | # when building with Visual Studio 20 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 21 | 22 | # find dependencies 23 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) 24 | 25 | find_package(MPI REQUIRED) 26 | 27 | find_package(Kokkos 3 REQUIRED) 28 | 29 | find_package(HYPRE) 30 | if(HYPRE_FOUND) 31 | set(CAJITA_HAVE_HYPRE ON) 32 | endif() 33 | 34 | find_package(Heffte) 35 | if(Heffte_FOUND) 36 | set(CAJITA_HAVE_HEFFTE ON) 37 | endif() 38 | 39 | # set the configure files 40 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/Cajita_Config.hpp.in 41 | ${CMAKE_CURRENT_BINARY_DIR}/include/Cajita_Config.hpp) 42 | 43 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/CajitaSettings.cmake.in 44 | ${CMAKE_CURRENT_BINARY_DIR}/CajitaSettings.cmake 45 | @ONLY) 46 | 47 | # obtain the repository hash if this is a git repo 48 | add_custom_target( 49 | record_hash ALL VERBATIM 50 | COMMAND ${CMAKE_COMMAND} 51 | -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} 52 | -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} 53 | -DCAJITA_VERSION_STRING=${CAJITA_VERSION_STRING} 54 | -P cmake/SetupVersion.cmake 55 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 56 | 57 | # also run the command when configuring 58 | execute_process( 59 | COMMAND ${CMAKE_COMMAND} 60 | -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} 61 | -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} 62 | -DCAJITA_VERSION_STRING=${CAJITA_VERSION_STRING} 63 | -P cmake/SetupVersion.cmake 64 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 65 | 66 | # add code coverage build 67 | option(Cajita_ENABLE_COVERAGE_BUILD "Do a coverage build" OFF) 68 | if(Cajita_ENABLE_COVERAGE_BUILD) 69 | message(STATUS "Enabling coverage build") 70 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -O0") 71 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") 72 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") 73 | endif() 74 | 75 | # create the library 76 | add_subdirectory(src) 77 | 78 | # installation configuration files 79 | include(CMakePackageConfigHelpers) 80 | 81 | configure_package_config_file(cmake/CajitaConfig.cmake.in 82 | ${CMAKE_CURRENT_BINARY_DIR}/CajitaConfig.cmake 83 | INSTALL_DESTINATION lib/cmake/Cajita 84 | ) 85 | 86 | install(FILES 87 | ${CMAKE_CURRENT_BINARY_DIR}/CajitaConfig.cmake 88 | ${CMAKE_CURRENT_BINARY_DIR}/CajitaSettings.cmake 89 | DESTINATION lib/cmake/Cajita ) 90 | 91 | if(HYPRE_FOUND) 92 | install(FILES 93 | ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindHYPRE.cmake 94 | DESTINATION lib/cmake/Cajita ) 95 | endif() 96 | 97 | install(DIRECTORY ${PROJECT_BINARY_DIR}/include/ 98 | DESTINATION include 99 | FILES_MATCHING PATTERN "*.hpp") 100 | 101 | # add tests 102 | option(Cajita_ENABLE_TESTING "Build tests" OFF) 103 | 104 | if(Cajita_ENABLE_TESTING) 105 | enable_testing() 106 | add_subdirectory(unit_test) 107 | endif() 108 | 109 | # clang format 110 | find_package(CLANG_FORMAT) 111 | if(CLANG_FORMAT_FOUND) 112 | file(GLOB_RECURSE FORMAT_SOURCES src/*.cpp src/*.hpp) 113 | add_custom_target(format 114 | COMMAND ${CLANG_FORMAT_EXECUTABLE} -i -style=file ${FORMAT_SOURCES} 115 | DEPENDS ${FORMAT_SOURCES}) 116 | endif() 117 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright 2019-2020 the Cajita authors 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cajita has moved and is now being actively developed as part of the [Cabana 2 | library](https://github.com/ECP-copa/Cabana/tree/master/cajita) 3 | 4 | This repository will no longer be updated. 5 | 6 | ## Cajita 7 | 8 | A library for computations on logically rectilinear grids 9 | 10 | ### [LICENSE](https://github.com/ECP-copa/Cajita/blob/master/LICENSE) 11 | 12 | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) 13 | -------------------------------------------------------------------------------- /cmake/CajitaConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH}) 4 | 5 | find_package(MPI REQUIRED) 6 | find_package(Kokkos 3 REQUIRED) 7 | find_package(HYPRE) 8 | find_package(Heffte) 9 | 10 | include("${CMAKE_CURRENT_LIST_DIR}/CajitaTargets.cmake") 11 | check_required_components(Cajita) 12 | -------------------------------------------------------------------------------- /cmake/CajitaSettings.cmake.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ECP-copa/Cajita/b1976a99a9442167430d4765b2e9b7be7ae0575b/cmake/CajitaSettings.cmake.in -------------------------------------------------------------------------------- /cmake/FindCLANG_FORMAT.cmake: -------------------------------------------------------------------------------- 1 | # Find clang-format 2 | # 3 | # CLANG_FORMAT_EXECUTABLE - Path to clang-format executable 4 | # CLANG_FORMAT_FOUND - True if the clang-format executable was found. 5 | # CLANG_FORMAT_VERSION - The version of clang-format found 6 | # 7 | 8 | find_program(CLANG_FORMAT_EXECUTABLE 9 | NAMES clang-format 10 | clang-format-7 11 | clang-format-6.0 12 | clang-format-5.0 13 | clang-format-4.0 14 | clang-format-3.9 15 | clang-format-3.8 16 | clang-format-3.7 17 | clang-format-3.6 18 | clang-format-3.5 19 | clang-format-3.4 20 | clang-format-3.3 21 | DOC "clang-format executable") 22 | mark_as_advanced(CLANG_FORMAT_EXECUTABLE) 23 | 24 | # Extract version from command "clang-format -version" 25 | if(CLANG_FORMAT_EXECUTABLE) 26 | execute_process(COMMAND ${CLANG_FORMAT_EXECUTABLE} -version 27 | OUTPUT_VARIABLE clang_format_version 28 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) 29 | 30 | if(clang_format_version MATCHES "^clang-format version .*") 31 | # clang_format_version sample: "clang-format version 3.9.1-4ubuntu3~16.04.1 32 | # (tags/RELEASE_391/rc2)" 33 | string(REGEX 34 | REPLACE "clang-format version ([.0-9]+).*" 35 | "\\1" 36 | CLANG_FORMAT_VERSION 37 | "${clang_format_version}") 38 | # CLANG_FORMAT_VERSION sample: "3.9.1" 39 | else() 40 | set(CLANG_FORMAT_VERSION 0.0) 41 | endif() 42 | else() 43 | set(CLANG_FORMAT_VERSION 0.0) 44 | endif() 45 | 46 | include(FindPackageHandleStandardArgs) 47 | # handle the QUIETLY and REQUIRED arguments and set CLANG_FORMAT_FOUND to TRUE 48 | # if all listed variables are TRUE 49 | find_package_handle_standard_args(CLANG_FORMAT REQUIRED_VARS CLANG_FORMAT_EXECUTABLE VERSION_VAR CLANG_FORMAT_VERSION) 50 | -------------------------------------------------------------------------------- /cmake/FindHYPRE.cmake: -------------------------------------------------------------------------------- 1 | find_package(PkgConfig QUIET) 2 | pkg_check_modules(PC_HYPRE QUIET hypre) 3 | 4 | find_path(HYPRE_INCLUDE_DIR 5 | NAMES HYPRE.h 6 | PATHS ${PC_HYPRE_INCLUDE_DIRS}) 7 | find_library(HYPRE_LIBRARY 8 | NAMES HYPRE 9 | PATHS ${PC_HYPRE_LIBRARY_DIRS}) 10 | 11 | include(FindPackageHandleStandardArgs) 12 | 13 | find_package_handle_standard_args(HYPRE 14 | FOUND_VAR HYPRE_FOUND 15 | REQUIRED_VARS 16 | HYPRE_LIBRARY 17 | HYPRE_INCLUDE_DIR 18 | VERSION_VAR HYPRE_VERSION 19 | ) 20 | 21 | if(HYPRE_INCLUDE_DIR AND HYPRE_LIBRARY) 22 | add_library(HYPRE::hypre UNKNOWN IMPORTED) 23 | set_target_properties(HYPRE::hypre PROPERTIES 24 | IMPORTED_LOCATION ${HYPRE_LIBRARY} 25 | INTERFACE_INCLUDE_DIRECTORIES ${HYPRE_INCLUDE_DIR}) 26 | endif() 27 | 28 | mark_as_advanced( 29 | HYPRE_INCLUDE_DIR 30 | HYPRE_LIBRARY ) 31 | -------------------------------------------------------------------------------- /cmake/SetupVersion.cmake: -------------------------------------------------------------------------------- 1 | SET(CAJITA_GIT_COMMIT_HASH "No hash available") 2 | 3 | IF(EXISTS ${SOURCE_DIR}/.git) 4 | FIND_PACKAGE(Git QUIET) 5 | IF(GIT_FOUND) 6 | EXECUTE_PROCESS( 7 | COMMAND ${GIT_EXECUTABLE} log --pretty=format:%h -n 1 8 | OUTPUT_VARIABLE CAJITA_GIT_COMMIT_HASH) 9 | ENDIF() 10 | ENDIF() 11 | MESSAGE("Cajita hash = '${CAJITA_GIT_COMMIT_HASH}'") 12 | 13 | configure_file(${SOURCE_DIR}/src/Cajita_Version.hpp.in 14 | ${BINARY_DIR}/include/Cajita_Version.hpp) 15 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADERS_PUBLIC 2 | Cajita.hpp 3 | Cajita_Array.hpp 4 | Cajita_LocalGrid.hpp 5 | Cajita_BovWriter.hpp 6 | Cajita_GlobalGrid.hpp 7 | Cajita_GlobalMesh.hpp 8 | Cajita_Halo.hpp 9 | Cajita_IndexSpace.hpp 10 | Cajita_Interpolation.hpp 11 | Cajita_LocalMesh.hpp 12 | Cajita_ManualPartitioner.hpp 13 | Cajita_MpiTraits.hpp 14 | Cajita_Partitioner.hpp 15 | Cajita_ReferenceStructuredSolver.hpp 16 | Cajita_Splines.hpp 17 | Cajita_Types.hpp 18 | Cajita_UniformDimPartitioner.hpp 19 | ) 20 | 21 | set(SOURCES_PUBLIC 22 | Cajita_LocalGrid.cpp 23 | Cajita_GlobalGrid.cpp 24 | Cajita_GlobalMesh.cpp 25 | Cajita_IndexSpace.cpp 26 | Cajita_ManualPartitioner.cpp 27 | Cajita_UniformDimPartitioner.cpp 28 | ) 29 | 30 | if(HYPRE_FOUND) 31 | list(APPEND HEADERS_PUBLIC 32 | Cajita_HypreStructuredSolver.hpp 33 | ) 34 | endif() 35 | 36 | if(CAJITA_HAVE_HEFFTE) 37 | list(APPEND HEADERS_PUBLIC 38 | Cajita_FastFourierTransform.hpp 39 | ) 40 | endif() 41 | 42 | add_library(Cajita ${SOURCES_PUBLIC}) 43 | 44 | target_link_libraries(Cajita 45 | Kokkos::kokkos 46 | MPI::MPI_CXX 47 | ) 48 | 49 | if(HYPRE_FOUND) 50 | target_link_libraries(Cajita HYPRE::hypre) 51 | endif() 52 | 53 | if(Heffte_FOUND) 54 | target_link_libraries(Cajita Heffte::Heffte) 55 | endif() 56 | 57 | install(FILES ${HEADERS_PUBLIC} 58 | DESTINATION include) 59 | 60 | target_include_directories(Cajita 61 | PUBLIC 62 | $ 63 | $ 64 | $ 65 | ) 66 | 67 | install(TARGETS Cajita 68 | EXPORT CajitaTargets 69 | ARCHIVE LIBRARY PUBLIC_HEADER 70 | ARCHIVE DESTINATION lib 71 | ) 72 | 73 | install(EXPORT CajitaTargets 74 | NAMESPACE Cajita:: 75 | DESTINATION lib/cmake/Cajita 76 | ) 77 | -------------------------------------------------------------------------------- /src/Cajita.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_HPP 13 | #define CAJITA_HPP 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef CAJITA_HAVE_HYPRE 35 | #include 36 | #endif 37 | 38 | #ifdef CAJITA_HAVE_HEFFTE 39 | #include 40 | #endif 41 | 42 | #endif // end CAJITA_HPP 43 | -------------------------------------------------------------------------------- /src/Cajita_BovWriter.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_BOVWRITER_HPP 13 | #define CAJITA_BOVWRITER_HPP 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace Cajita 33 | { 34 | namespace BovWriter 35 | { 36 | //---------------------------------------------------------------------------// 37 | // VisIt Brick-of-Values (BOV) grid field writer. 38 | //---------------------------------------------------------------------------// 39 | // BOV Format traits. 40 | template 41 | struct BovFormat; 42 | 43 | template <> 44 | struct BovFormat 45 | { 46 | static std::string value() { return "SHORT"; } 47 | }; 48 | 49 | template <> 50 | struct BovFormat 51 | { 52 | static std::string value() { return "INT"; } 53 | }; 54 | 55 | template <> 56 | struct BovFormat 57 | { 58 | static std::string value() { return "FLOAT"; } 59 | }; 60 | 61 | template <> 62 | struct BovFormat 63 | { 64 | static std::string value() { return "DOUBLE"; } 65 | }; 66 | 67 | // BOV Centering 68 | template 69 | struct BovCentering; 70 | 71 | template <> 72 | struct BovCentering 73 | { 74 | static std::string value() { return "zonal"; } 75 | }; 76 | 77 | template <> 78 | struct BovCentering 79 | { 80 | static std::string value() { return "nodal"; } 81 | }; 82 | 83 | //---------------------------------------------------------------------------// 84 | // Create the MPI subarray for the given array. 85 | template 86 | MPI_Datatype createSubarray( const Array_t &array, 87 | const std::array &owned_extents, 88 | const std::array &global_extents ) 89 | { 90 | using value_type = typename Array_t::value_type; 91 | const auto &global_grid = array.layout()->localGrid()->globalGrid(); 92 | 93 | int local_start[4] = { 94 | static_cast( global_grid.globalOffset( Dim::K ) ), 95 | static_cast( global_grid.globalOffset( Dim::J ) ), 96 | static_cast( global_grid.globalOffset( Dim::I ) ), 0}; 97 | int local_size[4] = {static_cast( owned_extents[Dim::K] ), 98 | static_cast( owned_extents[Dim::J] ), 99 | static_cast( owned_extents[Dim::I] ), 100 | static_cast( owned_extents[3] )}; 101 | int global_size[4] = {static_cast( global_extents[Dim::K] ), 102 | static_cast( global_extents[Dim::J] ), 103 | static_cast( global_extents[Dim::I] ), 104 | static_cast( global_extents[3] )}; 105 | 106 | MPI_Datatype subarray; 107 | MPI_Type_create_subarray( 4, global_size, local_size, local_start, 108 | MPI_ORDER_C, MpiTraits::type(), 109 | &subarray ); 110 | 111 | return subarray; 112 | } 113 | 114 | //---------------------------------------------------------------------------// 115 | /*! 116 | \brief Write a grid array to a VisIt BOV. 117 | 118 | This version writes a single output and does not use bricklets. We will do 119 | this in the future to improve parallel visualization. 120 | 121 | \param time_step_index The index of the time step we are writing. 122 | \param time The current time 123 | \param array The array to write 124 | */ 125 | template 126 | void writeTimeStep( const int time_step_index, const double time, 127 | const Array_t &array ) 128 | { 129 | static_assert( isUniformMesh::value, 130 | "ViSIT BOV writer can only be used with uniform mesh" ); 131 | 132 | // Types 133 | using entity_type = typename Array_t::entity_type; 134 | using value_type = typename Array_t::value_type; 135 | using device_type = typename Array_t::device_type; 136 | using execution_space = typename device_type::execution_space; 137 | 138 | // Get the global grid. 139 | const auto &global_grid = array.layout()->localGrid()->globalGrid(); 140 | 141 | // Get the global mesh. 142 | const auto &global_mesh = global_grid.globalMesh(); 143 | 144 | // If this is a node field, determine periodicity so we can add the last 145 | // node back to the visualization if needed. 146 | std::array global_extents = {-1, -1, -1, -1}; 147 | for ( int d = 0; d < 3; ++d ) 148 | { 149 | if ( std::is_same::value ) 150 | global_extents[d] = global_grid.globalNumEntity( Cell(), d ); 151 | else if ( std::is_same::value ) 152 | global_extents[d] = global_grid.globalNumEntity( Cell(), d ) + 1; 153 | } 154 | global_extents[3] = array.layout()->dofsPerEntity(); 155 | auto owned_index_space = array.layout()->indexSpace( Own(), Local() ); 156 | std::array owned_extents = {-1, -1, -1, -1}; 157 | for ( int d = 0; d < 3; ++d ) 158 | { 159 | if ( std::is_same::value ) 160 | { 161 | owned_extents[d] = owned_index_space.extent( d ); 162 | } 163 | else if ( std::is_same::value ) 164 | { 165 | if ( !global_grid.isPeriodic( d ) || 166 | global_grid.dimBlockId( d ) < 167 | global_grid.dimNumBlock( d ) - 1 ) 168 | owned_extents[d] = owned_index_space.extent( d ); 169 | else 170 | owned_extents[d] = owned_index_space.extent( d ) + 1; 171 | } 172 | } 173 | owned_extents[3] = array.layout()->dofsPerEntity(); 174 | 175 | // Create a contiguous array of the owned array values. Note that we 176 | // reorder to KJI grid ordering to conform to the BOV format. 177 | IndexSpace<4> local_space( 178 | {owned_index_space.min( Dim::I ), owned_index_space.min( Dim::J ), 179 | owned_index_space.min( Dim::K ), 0}, 180 | {owned_index_space.min( Dim::I ) + owned_extents[Dim::I], 181 | owned_index_space.min( Dim::J ) + owned_extents[Dim::J], 182 | owned_index_space.min( Dim::K ) + owned_extents[Dim::K], 183 | owned_extents[3]} ); 184 | auto owned_subview = createSubview( array.view(), local_space ); 185 | IndexSpace<4> reorder_space( {owned_extents[Dim::K], owned_extents[Dim::J], 186 | owned_extents[Dim::I], owned_extents[3]} ); 187 | auto owned_view = createView( 188 | array.label(), reorder_space ); 189 | Kokkos::parallel_for( 190 | "bov_reorder", 191 | createExecutionPolicy( reorder_space, execution_space() ), 192 | KOKKOS_LAMBDA( const int k, const int j, const int i, const int l ) { 193 | owned_view( k, j, i, l ) = owned_subview( i, j, k, l ); 194 | } ); 195 | 196 | // Compose a data file name prefix. 197 | std::stringstream file_name; 198 | file_name << "grid_" << array.label() << "_" << std::setfill( '0' ) 199 | << std::setw( 6 ) << time_step_index; 200 | 201 | // Open a binary data file. 202 | std::string data_file_name = file_name.str() + ".dat"; 203 | MPI_File data_file; 204 | MPI_File_open( global_grid.comm(), data_file_name.c_str(), 205 | MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, 206 | &data_file ); 207 | 208 | // Create the global subarray in which we are writing the local data. 209 | auto subarray = createSubarray( array, owned_extents, global_extents ); 210 | MPI_Type_commit( &subarray ); 211 | 212 | // Set the data in the file this process is going to write to. 213 | MPI_File_set_view( data_file, 0, MpiTraits::type(), subarray, 214 | "native", MPI_INFO_NULL ); 215 | 216 | // Write the view to binary. 217 | MPI_Status status; 218 | MPI_File_write_all( data_file, owned_view.data(), owned_view.size(), 219 | MpiTraits::type(), &status ); 220 | 221 | // Clean up. 222 | MPI_File_close( &data_file ); 223 | MPI_Type_free( &subarray ); 224 | 225 | // Create a VisIt BOV header with global data. Only create the header 226 | // on rank 0. 227 | int rank; 228 | MPI_Comm_rank( global_grid.comm(), &rank ); 229 | if ( 0 == rank ) 230 | { 231 | // Open a file for writing. 232 | std::string header_file_name = file_name.str() + ".bov"; 233 | std::fstream header; 234 | header.open( header_file_name, std::fstream::out ); 235 | 236 | // Write the current time. 237 | header << "TIME: " << time << std::endl; 238 | 239 | // Data file name. 240 | header << "DATA_FILE: " << data_file_name << std::endl; 241 | 242 | // Global data size. 243 | header << "DATA_SIZE: " << global_extents[Dim::I] << " " 244 | << global_extents[Dim::J] << " " << global_extents[Dim::K] 245 | << std::endl; 246 | 247 | // Data format. 248 | header << "DATA_FORMAT: " << BovFormat::value() 249 | << std::endl; 250 | 251 | // Variable name. 252 | header << "VARIABLE: " << array.label() << std::endl; 253 | 254 | // Endian order 255 | header << "DATA_ENDIAN: LITTLE" << std::endl; 256 | 257 | // Data location. 258 | header << "CENTERING: " << BovCentering::value() 259 | << std::endl; 260 | 261 | // Mesh low corner. 262 | header << "BRICK_ORIGIN: " << global_mesh.lowCorner( Dim::I ) << " " 263 | << global_mesh.lowCorner( Dim::J ) << " " 264 | << global_mesh.lowCorner( Dim::K ) << std::endl; 265 | 266 | // Mesh global width 267 | header << "BRICK_SIZE: " 268 | << global_grid.globalNumEntity( Cell(), Dim::I ) * 269 | global_mesh.cellSize( Dim::I ) 270 | << " " 271 | << global_grid.globalNumEntity( Cell(), Dim::J ) * 272 | global_mesh.cellSize( Dim::J ) 273 | << " " 274 | << global_grid.globalNumEntity( Cell(), Dim::K ) * 275 | global_mesh.cellSize( Dim::K ) 276 | << std::endl; 277 | 278 | // Number of data components. Scalar and vector types are 279 | // supported. 280 | header << "DATA_COMPONENTS: " << global_extents[3] << std::endl; 281 | 282 | // Close the header. 283 | header.close(); 284 | } 285 | } 286 | 287 | //---------------------------------------------------------------------------// 288 | 289 | } // end namespace BovWriter 290 | } // end namespace Cajita 291 | 292 | #endif // end CAJITA_BOVWRITER_HPP 293 | -------------------------------------------------------------------------------- /src/Cajita_Config.hpp.in: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_CONFIG_HPP 13 | #define CAJITA_CONFIG_HPP 14 | 15 | #cmakedefine CAJITA_HAVE_HYPRE 16 | #cmakedefine CAJITA_HAVE_HEFFTE 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/Cajita_FastFourierTransform.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_FASTFOURIERTRANSFORM_HPP 13 | #define CAJITA_FASTFOURIERTRANSFORM_HPP 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace Cajita 27 | { 28 | namespace Experimental 29 | { 30 | //---------------------------------------------------------------------------// 31 | template 32 | struct HeffteMemoryTraits; 33 | 34 | #ifdef KOKKOS_ENABLE_CUDA 35 | template <> 36 | struct HeffteMemoryTraits 37 | { 38 | static constexpr heffte_memory_type_t value = HEFFTE_MEM_GPU; 39 | }; 40 | 41 | template <> 42 | struct HeffteMemoryTraits 43 | { 44 | static constexpr heffte_memory_type_t value = HEFFTE_MEM_MANAGED; 45 | }; 46 | #endif 47 | 48 | template <> 49 | struct HeffteMemoryTraits 50 | { 51 | static constexpr heffte_memory_type_t value = HEFFTE_MEM_CPU; 52 | }; 53 | 54 | //---------------------------------------------------------------------------// 55 | class FastFourierTransformParams 56 | { 57 | public: 58 | /*! 59 | \brief Default constructor to disable aggregate initialization. 60 | */ 61 | FastFourierTransformParams() 62 | : collective( 2 ) 63 | , exchange( 0 ) 64 | , packflag( 2 ) 65 | , scaled( 1 ) 66 | { 67 | } 68 | 69 | /*! 70 | \brief Set the collective type. 71 | \param type Collective type. 72 | 73 | 0: point-to-point 74 | 1: all-to-all 75 | 2: combination 76 | 77 | If this function is not used to set the type then 2 is used as the 78 | default. 79 | */ 80 | FastFourierTransformParams &setCollectiveType( const int type ) 81 | { 82 | collective = type; 83 | return *this; 84 | } 85 | 86 | /*! 87 | \brief Set the exchange type. 88 | \param type Exchange type. 89 | 90 | 0: reshape direct from pencil to pencil 91 | 1: two reshapes from pencil to brick, then brick to pencil 92 | 93 | If this function is not used to set the type then 0 is used as the 94 | default. 95 | */ 96 | FastFourierTransformParams &setExchangeType( const int type ) 97 | { 98 | exchange = type; 99 | return *this; 100 | } 101 | 102 | /*! 103 | \brief Set the pack type. 104 | \param type Pack type. 105 | 106 | 0: array 107 | 1: pointer 108 | 2: memcpy 109 | 110 | If this function is not used to set the type then 2 is used as the 111 | default. 112 | */ 113 | FastFourierTransformParams &setPackType( const int type ) 114 | { 115 | packflag = type; 116 | return *this; 117 | } 118 | 119 | /*! 120 | \brief Set the scaling type. 121 | \param type Scaling type. 122 | 123 | 0: Not scaling after forward 124 | 1: Scaling after forward 125 | 126 | If this function is not used to set the type then 1 is used as the 127 | default. 128 | */ 129 | FastFourierTransformParams &setScalingType( const int type ) 130 | { 131 | scaled = type; 132 | return *this; 133 | } 134 | 135 | // Collective communication type. 136 | int collective; 137 | 138 | // Parallel decomposition exchange type. 139 | int exchange; 140 | 141 | // Buffer packing type. 142 | int packflag; 143 | 144 | // Forward scaling option. 145 | int scaled; 146 | }; 147 | 148 | //---------------------------------------------------------------------------// 149 | template 150 | class FastFourierTransform 151 | { 152 | public: 153 | // Types. 154 | using value_type = Scalar; 155 | using entity_type = EntityType; 156 | using mesh_type = MeshType; 157 | using device_type = DeviceType; 158 | 159 | /*! 160 | \brief Constructor 161 | \param layout The array layout defining the vector space of the 162 | transform. 163 | \param params Parameters for the 3D FFT. 164 | */ 165 | FastFourierTransform( const ArrayLayout &layout, 166 | const FastFourierTransformParams ¶ms ) 167 | : _fft( layout.localGrid()->globalGrid().comm() ) 168 | { 169 | if ( 1 != layout.dofsPerEntity() ) 170 | throw std::logic_error( 171 | "Only 1 complex value per entity allowed in FFT" ); 172 | 173 | // Set the memory type. For now we will just do FFTs on the host until 174 | // we find the HEFFTE GPU memory bug. 175 | _fft.mem_type = HEFFTE_MEM_CPU; 176 | 177 | // Let the fft allocate its own send/receive buffers. 178 | _fft.memoryflag = 1; 179 | 180 | // Set parameters. 181 | _fft.collective = params.collective; 182 | _fft.exchange = params.exchange; 183 | _fft.packflag = params.packflag; 184 | _fft.scaled = params.scaled; 185 | 186 | // Get the global grid. 187 | const auto &global_grid = layout.localGrid()->globalGrid(); 188 | 189 | // Get the global dimensions of the problem. K indices move the 190 | // fastest because we fix the work array to be layout right. 191 | std::array global_num_entity = { 192 | global_grid.globalNumEntity( EntityType(), Dim::K ), 193 | global_grid.globalNumEntity( EntityType(), Dim::J ), 194 | global_grid.globalNumEntity( EntityType(), Dim::I )}; 195 | 196 | // Get the local dimensions of the problem. 197 | auto entity_space = 198 | layout.localGrid()->indexSpace( Own(), EntityType(), Local() ); 199 | std::array local_num_entity = { 200 | (int)entity_space.extent( Dim::K ), 201 | (int)entity_space.extent( Dim::J ), 202 | (int)entity_space.extent( Dim::I )}; 203 | 204 | // Get the low corner of the global index space on this rank. 205 | std::array global_low = { 206 | (int)global_grid.globalOffset( Dim::K ), 207 | (int)global_grid.globalOffset( Dim::J ), 208 | (int)global_grid.globalOffset( Dim::I )}; 209 | 210 | // Get the high corner of the global index space on this rank. 211 | std::array global_high = { 212 | global_low[Dim::I] + local_num_entity[Dim::I] - 1, 213 | global_low[Dim::J] + local_num_entity[Dim::J] - 1, 214 | global_low[Dim::K] + local_num_entity[Dim::K] - 1}; 215 | 216 | // Setup the fft. 217 | int permute = 0; 218 | int fftsize, sendsize, recvsize; 219 | _fft.setup( global_num_entity.data(), global_low.data(), 220 | global_high.data(), global_low.data(), global_high.data(), 221 | permute, fftsize, sendsize, recvsize ); 222 | 223 | // Check the size. 224 | if ( fftsize < (int)entity_space.size() ) 225 | throw std::logic_error( "HEFFTE expected allocation size smaller " 226 | "than local grid size" ); 227 | 228 | // Allocate the work array. 229 | _fft_work = Kokkos::View( 230 | Kokkos::ViewAllocateWithoutInitializing( "fft_work" ), 231 | 2 * fftsize ); 232 | } 233 | 234 | /*! 235 | \brief Do a forward FFT. 236 | \param in The array on which to perform the forward transform. 237 | */ 238 | template 239 | void forward( const Array_t &x ) 240 | { 241 | compute( x, 1 ); 242 | } 243 | 244 | /*! 245 | \brief Do a reverse FFT. 246 | \param out The array on which to perform the reverse transform. 247 | */ 248 | template 249 | void reverse( const Array_t &x ) 250 | { 251 | compute( x, -1 ); 252 | } 253 | 254 | public: 255 | template 256 | void compute( const Array_t &x, const int flag ) 257 | { 258 | static_assert( is_array::value, "Must use an array" ); 259 | static_assert( 260 | std::is_same::value, 261 | "Array entity type mush match transform entity type" ); 262 | static_assert( 263 | std::is_same::value, 264 | "Array mesh type mush match transform mesh type" ); 265 | static_assert( 266 | std::is_same::value, 267 | "Array device type and transform device type are different." ); 268 | static_assert( 269 | std::is_same>::value || 271 | std::is_same::value, 272 | "Array value type and complex transform value type are " 273 | "different." ); 274 | 275 | if ( 1 != x.layout()->dofsPerEntity() ) 276 | throw std::logic_error( 277 | "Only 1 complex value per entity allowed in FFT" ); 278 | 279 | // Create a subview of the work array to write the local data into. 280 | auto own_space = 281 | x.layout()->localGrid()->indexSpace( Own(), EntityType(), Local() ); 282 | auto work_view_space = appendDimension( own_space, 2 ); 283 | auto work_view = createView( 284 | work_view_space, _fft_work.data() ); 285 | 286 | // Copy to the work array. The work array only contains owned data. 287 | auto x_view = x.view(); 288 | Kokkos::parallel_for( 289 | "fft_copy_x_to_work", 290 | createExecutionPolicy( own_space, 291 | typename DeviceType::execution_space() ), 292 | KOKKOS_LAMBDA( const int i, const int j, const int k ) { 293 | auto iw = i - own_space.min( Dim::I ); 294 | auto jw = j - own_space.min( Dim::J ); 295 | auto kw = k - own_space.min( Dim::K ); 296 | work_view( iw, jw, kw, 0 ) = x_view( i, j, k, 0 ).real(); 297 | work_view( iw, jw, kw, 1 ) = x_view( i, j, k, 0 ).imag(); 298 | } ); 299 | 300 | // Copy to the host. Once we fix the HEFFTE GPU memory bug we wont 301 | // need this. 302 | auto fft_work_mirror = Kokkos::create_mirror_view_and_copy( 303 | Kokkos::HostSpace(), _fft_work ); 304 | 305 | // Perform FFT. 306 | _fft.compute( fft_work_mirror.data(), fft_work_mirror.data(), flag ); 307 | 308 | // Copy back to the work array. Once we fix the HEFFTE GPU memory bug 309 | // we wont need this. 310 | Kokkos::deep_copy( _fft_work, fft_work_mirror ); 311 | 312 | // Copy back to output array. 313 | Kokkos::parallel_for( 314 | "fft_copy_work_to_x", 315 | createExecutionPolicy( own_space, 316 | typename DeviceType::execution_space() ), 317 | KOKKOS_LAMBDA( const int i, const int j, const int k ) { 318 | auto iw = i - own_space.min( Dim::I ); 319 | auto jw = j - own_space.min( Dim::J ); 320 | auto kw = k - own_space.min( Dim::K ); 321 | x_view( i, j, k, 0 ).real() = work_view( iw, jw, kw, 0 ); 322 | x_view( i, j, k, 0 ).imag() = work_view( iw, jw, kw, 1 ); 323 | } ); 324 | } 325 | 326 | private: 327 | HEFFTE::FFT3d _fft; 328 | Kokkos::View _fft_work; 329 | }; 330 | 331 | //---------------------------------------------------------------------------// 332 | // FFT creation 333 | //---------------------------------------------------------------------------// 334 | template 335 | std::shared_ptr> 336 | createFastFourierTransform( const ArrayLayout &layout, 337 | const FastFourierTransformParams ¶ms ) 338 | { 339 | return std::make_shared< 340 | FastFourierTransform>( 341 | layout, params ); 342 | } 343 | 344 | //---------------------------------------------------------------------------// 345 | 346 | } // end namespace Experimental 347 | } // end namespace Cajita 348 | 349 | #endif // end CAJITA_FASTFOURIERTRANSFORM_HPP 350 | -------------------------------------------------------------------------------- /src/Cajita_GlobalGrid.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace Cajita 19 | { 20 | //---------------------------------------------------------------------------// 21 | // Constructor. 22 | template 23 | GlobalGrid::GlobalGrid( 24 | MPI_Comm comm, const std::shared_ptr> &global_mesh, 25 | const std::array &periodic, const Partitioner &partitioner ) 26 | : _global_mesh( global_mesh ) 27 | , _periodic( periodic ) 28 | { 29 | // Partition the problem. 30 | std::array global_num_cell = { 31 | _global_mesh->globalNumCell( Dim::I ), 32 | _global_mesh->globalNumCell( Dim::J ), 33 | _global_mesh->globalNumCell( Dim::K )}; 34 | _ranks_per_dim = partitioner.ranksPerDimension( comm, global_num_cell ); 35 | 36 | // Extract the periodicity of the boundary as integers. 37 | std::array periodic_dims = {_periodic[Dim::I], _periodic[Dim::J], 38 | _periodic[Dim::K]}; 39 | 40 | // Generate a communicator with a Cartesian topology. 41 | int reorder_cart_ranks = 1; 42 | MPI_Cart_create( comm, 3, _ranks_per_dim.data(), periodic_dims.data(), 43 | reorder_cart_ranks, &_cart_comm ); 44 | 45 | // Get the Cartesian topology index of this rank. 46 | int linear_rank; 47 | MPI_Comm_rank( _cart_comm, &linear_rank ); 48 | MPI_Cart_coords( _cart_comm, linear_rank, 3, _cart_rank.data() ); 49 | 50 | // Get the cells per dimension and the remainder. 51 | std::array cells_per_dim; 52 | std::array dim_remainder; 53 | for ( int d = 0; d < 3; ++d ) 54 | { 55 | cells_per_dim[d] = global_num_cell[d] / _ranks_per_dim[d]; 56 | dim_remainder[d] = global_num_cell[d] % _ranks_per_dim[d]; 57 | } 58 | 59 | // Compute the global cell offset and the local low corner on this rank by 60 | // computing the starting global cell index via exclusive scan. 61 | _global_cell_offset = {0, 0, 0}; 62 | for ( int d = 0; d < 3; ++d ) 63 | { 64 | for ( int r = 0; r < _cart_rank[d]; ++r ) 65 | { 66 | _global_cell_offset[d] += cells_per_dim[d]; 67 | if ( dim_remainder[d] > r ) 68 | ++_global_cell_offset[d]; 69 | } 70 | } 71 | 72 | // Compute the number of local cells in this rank in each dimension. 73 | for ( int d = 0; d < 3; ++d ) 74 | { 75 | _owned_num_cell[d] = cells_per_dim[d]; 76 | if ( dim_remainder[d] > _cart_rank[d] ) 77 | ++_owned_num_cell[d]; 78 | } 79 | } 80 | 81 | //---------------------------------------------------------------------------// 82 | // Destructor. 83 | template 84 | GlobalGrid::~GlobalGrid() 85 | { 86 | MPI_Comm_free( &_cart_comm ); 87 | } 88 | 89 | //---------------------------------------------------------------------------// 90 | // Get the grid communicator. 91 | template 92 | MPI_Comm GlobalGrid::comm() const 93 | { 94 | return _cart_comm; 95 | } 96 | 97 | //---------------------------------------------------------------------------// 98 | // Get the global mesh data. 99 | template 100 | const GlobalMesh &GlobalGrid::globalMesh() const 101 | { 102 | return *_global_mesh; 103 | } 104 | 105 | //---------------------------------------------------------------------------// 106 | // Get whether a given dimension is periodic. 107 | template 108 | bool GlobalGrid::isPeriodic( const int dim ) const 109 | { 110 | return _periodic[dim]; 111 | } 112 | 113 | //---------------------------------------------------------------------------// 114 | // Get the number of blocks in each dimension in the global mesh. 115 | template 116 | int GlobalGrid::dimNumBlock( const int dim ) const 117 | { 118 | return _ranks_per_dim[dim]; 119 | } 120 | 121 | //---------------------------------------------------------------------------// 122 | // Get the total number of blocks. 123 | template 124 | int GlobalGrid::totalNumBlock() const 125 | { 126 | int comm_size; 127 | MPI_Comm_size( _cart_comm, &comm_size ); 128 | return comm_size; 129 | } 130 | 131 | //---------------------------------------------------------------------------// 132 | // Get the id of this block in a given dimension. 133 | template 134 | int GlobalGrid::dimBlockId( const int dim ) const 135 | { 136 | return _cart_rank[dim]; 137 | } 138 | 139 | //---------------------------------------------------------------------------// 140 | // Get the id of this block. 141 | template 142 | int GlobalGrid::blockId() const 143 | { 144 | int comm_rank; 145 | MPI_Comm_rank( _cart_comm, &comm_rank ); 146 | return comm_rank; 147 | } 148 | 149 | //---------------------------------------------------------------------------// 150 | // Get the MPI rank of a block with the given indices. If the rank is out 151 | // of bounds and the boundary is not periodic, return -1 to indicate an 152 | // invalid rank. 153 | template 154 | int GlobalGrid::blockRank( const int i, const int j, 155 | const int k ) const 156 | { 157 | // Get the indices. 158 | std::array cr = {i, j, k}; 159 | 160 | // Check for invalid indices. An index is invalid if it is out of bounds 161 | // and the dimension is not periodic. An out of bound index in a periodic 162 | // dimension is valid because it will wrap around to a valid index. 163 | for ( int d = 0; d < 3; ++d ) 164 | if ( !_periodic[d] && ( cr[d] < 0 || _ranks_per_dim[d] <= cr[d] ) ) 165 | return -1; 166 | 167 | // If we have indices get their rank. 168 | int lr; 169 | MPI_Cart_rank( _cart_comm, cr.data(), &lr ); 170 | return lr; 171 | } 172 | 173 | //---------------------------------------------------------------------------// 174 | // Get the global number of cells in a given dimension. 175 | template 176 | int GlobalGrid::globalNumEntity( Cell, const int dim ) const 177 | { 178 | return _global_mesh->globalNumCell( dim ); 179 | } 180 | 181 | //---------------------------------------------------------------------------// 182 | // Get the global number of nodes in a given dimension. 183 | template 184 | int GlobalGrid::globalNumEntity( Node, const int dim ) const 185 | { 186 | // If this dimension is periodic that last node in the dimension is 187 | // repeated across the periodic boundary. 188 | if ( _periodic[dim] ) 189 | return globalNumEntity( Cell(), dim ); 190 | else 191 | return globalNumEntity( Cell(), dim ) + 1; 192 | } 193 | 194 | //---------------------------------------------------------------------------// 195 | // Get the global number of I-faces in a given dimension. 196 | template 197 | int GlobalGrid::globalNumEntity( Face, const int dim ) const 198 | { 199 | return ( Dim::I == dim ) ? globalNumEntity( Node(), dim ) 200 | : globalNumEntity( Cell(), dim ); 201 | } 202 | 203 | //---------------------------------------------------------------------------// 204 | // Get the global number of J-faces in a given dimension. 205 | template 206 | int GlobalGrid::globalNumEntity( Face, const int dim ) const 207 | { 208 | return ( Dim::J == dim ) ? globalNumEntity( Node(), dim ) 209 | : globalNumEntity( Cell(), dim ); 210 | } 211 | 212 | //---------------------------------------------------------------------------// 213 | // Get the global number of K-faces in a given dimension. 214 | template 215 | int GlobalGrid::globalNumEntity( Face, const int dim ) const 216 | { 217 | return ( Dim::K == dim ) ? globalNumEntity( Node(), dim ) 218 | : globalNumEntity( Cell(), dim ); 219 | } 220 | 221 | //---------------------------------------------------------------------------// 222 | // Get the global number of I-edges in a given dimension. 223 | template 224 | int GlobalGrid::globalNumEntity( Edge, const int dim ) const 225 | { 226 | return ( Dim::I == dim ) ? globalNumEntity( Cell(), dim ) 227 | : globalNumEntity( Node(), dim ); 228 | } 229 | 230 | //---------------------------------------------------------------------------// 231 | // Get the global number of J-edges in a given dimension. 232 | template 233 | int GlobalGrid::globalNumEntity( Edge, const int dim ) const 234 | { 235 | return ( Dim::J == dim ) ? globalNumEntity( Cell(), dim ) 236 | : globalNumEntity( Node(), dim ); 237 | } 238 | 239 | //---------------------------------------------------------------------------// 240 | // Get the global number of K-edges in a given dimension. 241 | template 242 | int GlobalGrid::globalNumEntity( Edge, const int dim ) const 243 | { 244 | return ( Dim::K == dim ) ? globalNumEntity( Cell(), dim ) 245 | : globalNumEntity( Node(), dim ); 246 | } 247 | 248 | //---------------------------------------------------------------------------// 249 | // Get the owned number of cells in a given dimension. 250 | template 251 | int GlobalGrid::ownedNumCell( const int dim ) const 252 | { 253 | return _owned_num_cell[dim]; 254 | } 255 | 256 | //---------------------------------------------------------------------------// 257 | // Get the global offset in a given dimension for the entity of a given 258 | // type. This is where our block starts in the global indexing scheme. 259 | template 260 | int GlobalGrid::globalOffset( const int dim ) const 261 | { 262 | return _global_cell_offset[dim]; 263 | } 264 | 265 | //---------------------------------------------------------------------------// 266 | // Class explicit instantiations. 267 | 268 | template class GlobalGrid>; 269 | template class GlobalGrid>; 270 | 271 | template class GlobalGrid>; 272 | template class GlobalGrid>; 273 | 274 | //---------------------------------------------------------------------------// 275 | 276 | } // end namespace Cajita 277 | -------------------------------------------------------------------------------- /src/Cajita_GlobalGrid.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_GLOBALGRID_HPP 13 | #define CAJITA_GLOBALGRID_HPP 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | namespace Cajita 25 | { 26 | //---------------------------------------------------------------------------// 27 | // Global logical grid. 28 | //---------------------------------------------------------------------------// 29 | template 30 | class GlobalGrid 31 | { 32 | public: 33 | // Mesh type. 34 | using mesh_type = MeshType; 35 | 36 | /*! 37 | \brief Constructor. 38 | \param comm The communicator over which to define the grid. 39 | \param global_mesh The global mesh data. 40 | \param periodic Whether each logical dimension is periodic. 41 | \param partitioner The grid partitioner. 42 | */ 43 | GlobalGrid( MPI_Comm comm, 44 | const std::shared_ptr> &global_mesh, 45 | const std::array &periodic, 46 | const Partitioner &partitioner ); 47 | 48 | // Destructor. 49 | ~GlobalGrid(); 50 | 51 | // Get the communicator. This communicator was generated with a Cartesian 52 | // topology. 53 | MPI_Comm comm() const; 54 | 55 | // Get the global mesh data. 56 | const GlobalMesh &globalMesh() const; 57 | 58 | // Get whether a given dimension is periodic. 59 | bool isPeriodic( const int dim ) const; 60 | 61 | // Get the number of blocks in each dimension in the global mesh. 62 | int dimNumBlock( const int dim ) const; 63 | 64 | // Get the total number of blocks. 65 | int totalNumBlock() const; 66 | 67 | // Get the id of this block in a given dimension. 68 | int dimBlockId( const int dim ) const; 69 | 70 | // Get the id of this block. 71 | int blockId() const; 72 | 73 | // Get the MPI rank of a block with the given indices. If the rank is out 74 | // of bounds and the boundary is not periodic, return -1 to indicate an 75 | // invalid rank. 76 | int blockRank( const int i, const int j, const int k ) const; 77 | 78 | // Get the global number of entities in a given dimension. 79 | int globalNumEntity( Cell, const int dim ) const; 80 | int globalNumEntity( Node, const int dim ) const; 81 | int globalNumEntity( Face, const int dim ) const; 82 | int globalNumEntity( Face, const int dim ) const; 83 | int globalNumEntity( Face, const int dim ) const; 84 | int globalNumEntity( Edge, const int dim ) const; 85 | int globalNumEntity( Edge, const int dim ) const; 86 | int globalNumEntity( Edge, const int dim ) const; 87 | 88 | // Get the owned number of cells in a given dimension of this block. 89 | int ownedNumCell( const int dim ) const; 90 | 91 | // Get the global offset in a given dimension. This is where our block 92 | // starts in the global indexing scheme. 93 | int globalOffset( const int dim ) const; 94 | 95 | private: 96 | MPI_Comm _cart_comm; 97 | std::shared_ptr> _global_mesh; 98 | std::array _periodic; 99 | std::array _ranks_per_dim; 100 | std::array _cart_rank; 101 | std::array _owned_num_cell; 102 | std::array _global_cell_offset; 103 | }; 104 | 105 | //---------------------------------------------------------------------------// 106 | // Creation function. 107 | //---------------------------------------------------------------------------// 108 | /*! 109 | \brief Create a global grid. 110 | \param comm The communicator over which to define the grid. 111 | \param global_mesh The global mesh data. 112 | \param periodic Whether each logical dimension is periodic. 113 | \param partitioner The grid partitioner. 114 | */ 115 | template 116 | std::shared_ptr> createGlobalGrid( 117 | MPI_Comm comm, const std::shared_ptr> &global_mesh, 118 | const std::array &periodic, const Partitioner &partitioner ) 119 | { 120 | return std::make_shared>( comm, global_mesh, periodic, 121 | partitioner ); 122 | } 123 | 124 | //---------------------------------------------------------------------------// 125 | 126 | } // end namespace Cajita 127 | 128 | #endif // end CAJITA_GLOBALGRID_HPP 129 | -------------------------------------------------------------------------------- /src/Cajita_GlobalMesh.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | 14 | namespace Cajita 15 | { 16 | 17 | template class GlobalMesh>; 18 | template class GlobalMesh>; 19 | 20 | template class GlobalMesh>; 21 | template class GlobalMesh>; 22 | 23 | } // end namespace Cajita 24 | -------------------------------------------------------------------------------- /src/Cajita_GlobalMesh.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJTIA_GLOBALMESH_HPP 13 | #define CAJTIA_GLOBALMESH_HPP 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace Cajita 24 | { 25 | //---------------------------------------------------------------------------// 26 | // Forward declaration of global mesh. 27 | template 28 | class GlobalMesh; 29 | 30 | //---------------------------------------------------------------------------// 31 | // Global mesh partial specialization for uniform mesh. Uniform meshes are 32 | // rectilinear meshes where every cell in the mesh is identical. A cell is 33 | // described by its width in each dimension. 34 | template 35 | class GlobalMesh> 36 | { 37 | public: 38 | // Mesh type. 39 | using mesh_type = UniformMesh; 40 | 41 | // Scalar type. 42 | using scalar_type = Scalar; 43 | 44 | // Cell size constructor - special case where all cell dimensions are the 45 | // same. 46 | GlobalMesh( const std::array &global_low_corner, 47 | const std::array &global_high_corner, 48 | const Scalar cell_size ) 49 | : _global_low_corner( global_low_corner ) 50 | , _global_high_corner( global_high_corner ) 51 | , _cell_size( {cell_size, cell_size, cell_size} ) 52 | { 53 | // Check that the domain is evenly divisible by the cell size in each 54 | // dimension within round-off error. 55 | for ( int d = 0; d < 3; ++d ) 56 | { 57 | Scalar ext = globalNumCell( d ) * _cell_size[d]; 58 | if ( std::abs( ext - extent( d ) ) > 59 | Scalar( 100.0 ) * std::numeric_limits::epsilon() ) 60 | throw std::logic_error( 61 | "Extent not evenly divisible by uniform cell size" ); 62 | } 63 | } 64 | 65 | // Cell size constructor - each cell dimension can be different. 66 | GlobalMesh( const std::array &global_low_corner, 67 | const std::array &global_high_corner, 68 | const std::array &cell_size ) 69 | : _global_low_corner( global_low_corner ) 70 | , _global_high_corner( global_high_corner ) 71 | , _cell_size( cell_size ) 72 | { 73 | // Check that the domain is evenly divisible by the cell size in each 74 | // dimension within round-off error. 75 | for ( int d = 0; d < 3; ++d ) 76 | { 77 | Scalar ext = globalNumCell( d ) * _cell_size[d]; 78 | if ( std::abs( ext - extent( d ) ) > 79 | Scalar( 100.0 ) * std::numeric_limits::epsilon() ) 80 | throw std::logic_error( 81 | "Extent not evenly divisible by uniform cell size" ); 82 | } 83 | } 84 | 85 | // Number of global cells constructor. 86 | GlobalMesh( const std::array &global_low_corner, 87 | const std::array &global_high_corner, 88 | const std::array &global_num_cell ) 89 | : _global_low_corner( global_low_corner ) 90 | , _global_high_corner( global_high_corner ) 91 | { 92 | // Compute the cell size in each dimension. 93 | for ( int d = 0; d < 3; ++d ) 94 | _cell_size[d] = ( _global_high_corner[d] - _global_low_corner[d] ) / 95 | global_num_cell[d]; 96 | 97 | // Check that the domain is evenly divisible by the cell size in each 98 | // dimension within round-off error and that we got the expected 99 | // number of cells. 100 | for ( int d = 0; d < 3; ++d ) 101 | { 102 | Scalar ext = globalNumCell( d ) * _cell_size[d]; 103 | if ( std::abs( ext - extent( d ) ) > 104 | Scalar( 100.0 ) * std::numeric_limits::epsilon() ) 105 | throw std::logic_error( 106 | "Extent not evenly divisible by uniform cell size" ); 107 | if ( globalNumCell( d ) != global_num_cell[d] ) 108 | throw std::logic_error( "Global number of cells mismatch" ); 109 | } 110 | } 111 | 112 | // GLOBAL MESH INTERFACE 113 | 114 | // Get the global low corner of the mesh. 115 | Scalar lowCorner( const int dim ) const { return _global_low_corner[dim]; } 116 | 117 | // Get the global high corner of the mesh. 118 | Scalar highCorner( const int dim ) const 119 | { 120 | return _global_high_corner[dim]; 121 | } 122 | 123 | // Get the extent of a given dimension. 124 | Scalar extent( const int dim ) const 125 | { 126 | return highCorner( dim ) - lowCorner( dim ); 127 | } 128 | 129 | // Get the global numer of cells in a given dimension. 130 | int globalNumCell( const int dim ) const 131 | { 132 | return std::rint( extent( dim ) / _cell_size[dim] ); 133 | } 134 | 135 | // UNIFORM MESH SPECIFIC 136 | 137 | // Get the uniform cell size in a given dimension. 138 | Scalar cellSize( const int dim ) const { return _cell_size[dim]; } 139 | 140 | private: 141 | std::array _global_low_corner; 142 | std::array _global_high_corner; 143 | std::array _cell_size; 144 | }; 145 | 146 | // Creation function. 147 | template 148 | std::shared_ptr>> 149 | createUniformGlobalMesh( const std::array &global_low_corner, 150 | const std::array &global_high_corner, 151 | const Scalar cell_size ) 152 | { 153 | return std::make_shared>>( 154 | global_low_corner, global_high_corner, cell_size ); 155 | } 156 | 157 | template 158 | std::shared_ptr>> 159 | createUniformGlobalMesh( const std::array &global_low_corner, 160 | const std::array &global_high_corner, 161 | const std::array &cell_size ) 162 | { 163 | return std::make_shared>>( 164 | global_low_corner, global_high_corner, cell_size ); 165 | } 166 | 167 | template 168 | std::shared_ptr>> 169 | createUniformGlobalMesh( const std::array &global_low_corner, 170 | const std::array &global_high_corner, 171 | const std::array &global_num_cell ) 172 | { 173 | return std::make_shared>>( 174 | global_low_corner, global_high_corner, global_num_cell ); 175 | } 176 | 177 | //---------------------------------------------------------------------------// 178 | // Global mesh partial specialization for non-uniform mesh. Non-uniform meshes 179 | // have a list of node locations for each spatial dimension which describe a 180 | // rectilinear mesh that has arbitrary cell sizes - each cell can possibly be 181 | // different. 182 | template 183 | class GlobalMesh> 184 | { 185 | public: 186 | // Mesh type. 187 | using mesh_type = NonUniformMesh; 188 | 189 | // Scalar type. 190 | using scalar_type = Scalar; 191 | 192 | // Constructor. 193 | GlobalMesh( const std::vector &i_edges, 194 | const std::vector &j_edges, 195 | const std::vector &k_edges ) 196 | : _edges( {i_edges, j_edges, k_edges} ) 197 | { 198 | } 199 | 200 | // GLOBAL MESH INTERFACE 201 | 202 | // Get the global low corner of the mesh. 203 | Scalar lowCorner( const int dim ) const { return _edges[dim].front(); } 204 | 205 | // Get the global high corner of the mesh. 206 | Scalar highCorner( const int dim ) const { return _edges[dim].back(); } 207 | 208 | // Get the extent of a given dimension. 209 | Scalar extent( const int dim ) const 210 | { 211 | return highCorner( dim ) - lowCorner( dim ); 212 | } 213 | 214 | // Get the global numer of cells in a given dimension. 215 | int globalNumCell( const int dim ) const { return _edges[dim].size() - 1; } 216 | 217 | // NON-UNIFORM MESH SPECIFIC 218 | 219 | // Get the edge array in a given dimension. 220 | const std::vector &nonUniformEdge( const int dim ) const 221 | { 222 | return _edges[dim]; 223 | } 224 | 225 | private: 226 | std::array, 3> _edges; 227 | }; 228 | 229 | // Creation function. 230 | template 231 | std::shared_ptr>> 232 | createNonUniformGlobalMesh( const std::vector &i_edges, 233 | const std::vector &j_edges, 234 | const std::vector &k_edges ) 235 | { 236 | return std::make_shared>>( 237 | i_edges, j_edges, k_edges ); 238 | } 239 | 240 | //---------------------------------------------------------------------------// 241 | 242 | } // end namespace Cajita 243 | 244 | #endif // end CAJTIA_GLOBALMESH_HPP 245 | -------------------------------------------------------------------------------- /src/Cajita_IndexSpace.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | 14 | namespace Cajita 15 | { 16 | 17 | template class IndexSpace<1>; 18 | template class IndexSpace<2>; 19 | template class IndexSpace<3>; 20 | template class IndexSpace<4>; 21 | 22 | } // end namespace Cajita 23 | -------------------------------------------------------------------------------- /src/Cajita_IndexSpace.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_INDEXSPACE_HPP 13 | #define CAJITA_INDEXSPACE_HPP 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace Cajita 22 | { 23 | //---------------------------------------------------------------------------// 24 | /*! 25 | \class IndexSpace 26 | \brief Structured index space. 27 | */ 28 | template 29 | class IndexSpace 30 | { 31 | public: 32 | //! Number of dimensions. 33 | static constexpr long Rank = N; 34 | 35 | /*! 36 | \brief Initializer list size constructor. 37 | */ 38 | IndexSpace( const std::initializer_list &size ) 39 | { 40 | std::fill( _min.data(), _min.data() + Rank, 0 ); 41 | std::copy( size.begin(), size.end(), _max.data() ); 42 | } 43 | 44 | /*! 45 | \brief Initializer list range constructor. 46 | */ 47 | IndexSpace( const std::initializer_list &min, 48 | const std::initializer_list &max ) 49 | { 50 | std::copy( min.begin(), min.end(), _min.data() ); 51 | std::copy( max.begin(), max.end(), _max.data() ); 52 | } 53 | 54 | /*! 55 | \brief Vector size constructor. 56 | */ 57 | IndexSpace( const std::array &size ) 58 | { 59 | std::fill( _min.data(), _min.data() + Rank, 0 ); 60 | std::copy( size.begin(), size.end(), _max.data() ); 61 | } 62 | 63 | /*! 64 | \brief Vector range constructor. 65 | */ 66 | IndexSpace( const std::array &min, const std::array &max ) 67 | { 68 | std::copy( min.begin(), min.end(), _min.data() ); 69 | std::copy( max.begin(), max.end(), _max.data() ); 70 | } 71 | 72 | //! Comparison operator. 73 | bool operator==( const IndexSpace &rhs ) const 74 | { 75 | for ( long i = 0; i < N; ++i ) 76 | { 77 | if ( min( i ) != rhs.min( i ) || max( i ) != rhs.max( i ) ) 78 | return false; 79 | } 80 | return true; 81 | } 82 | 83 | //! Comparison operator. 84 | bool operator!=( const IndexSpace &rhs ) const 85 | { 86 | return !( operator==( rhs ) ); 87 | } 88 | 89 | //! Get the minimum index in a given dimension. 90 | KOKKOS_INLINE_FUNCTION 91 | long min( const long dim ) const { return _min[dim]; } 92 | 93 | //! Get the minimum indices in all dimensions. 94 | KOKKOS_INLINE_FUNCTION 95 | Kokkos::Array min() const { return _min; } 96 | 97 | //! Get the maximum index in a given dimension. 98 | KOKKOS_INLINE_FUNCTION 99 | long max( const long dim ) const { return _max[dim]; } 100 | 101 | //! Get the maximum indices in all dimensions. 102 | KOKKOS_INLINE_FUNCTION 103 | Kokkos::Array max() const { return _max; } 104 | 105 | //! Get the range of a given dimension. 106 | KOKKOS_INLINE_FUNCTION 107 | Kokkos::pair range( const long dim ) const 108 | { 109 | return Kokkos::tie( _min[dim], _max[dim] ); 110 | } 111 | 112 | //! Get the number of dimensions. 113 | KOKKOS_INLINE_FUNCTION 114 | long rank() const { return Rank; } 115 | 116 | //! Get the extent of a given dimension. 117 | KOKKOS_INLINE_FUNCTION 118 | long extent( const long dim ) const { return _max[dim] - _min[dim]; } 119 | 120 | //! Get the total size of the index space. 121 | KOKKOS_INLINE_FUNCTION 122 | long size() const 123 | { 124 | long size = 1; 125 | for ( long d = 0; d < Rank; ++d ) 126 | size *= extent( d ); 127 | return size; 128 | } 129 | 130 | private: 131 | // Minimum index bounds. 132 | Kokkos::Array _min; 133 | 134 | // Maximum index bounds. 135 | Kokkos::Array _max; 136 | }; 137 | 138 | //---------------------------------------------------------------------------// 139 | /*! 140 | \brief Create a multi-dimensional execution policy over an index space. 141 | 142 | Rank-1 specialization. 143 | */ 144 | template 145 | Kokkos::RangePolicy 146 | createExecutionPolicy( const IndexSpace<1> &index_space, 147 | const ExecutionSpace & ) 148 | { 149 | return Kokkos::RangePolicy( index_space.min( 0 ), 150 | index_space.max( 0 ) ); 151 | } 152 | 153 | //---------------------------------------------------------------------------// 154 | /*! 155 | \brief Create a multi-dimensional execution policy over an index space. 156 | */ 157 | template 158 | Kokkos::MDRangePolicy> 159 | createExecutionPolicy( const IndexSpace_t &index_space, const ExecutionSpace & ) 160 | { 161 | return Kokkos::MDRangePolicy>( 163 | index_space.min(), index_space.max() ); 164 | } 165 | 166 | //---------------------------------------------------------------------------// 167 | /*! 168 | \brief Given an index space create a view over the extent of that index 169 | space. 170 | 171 | Rank-1 specialization. 172 | */ 173 | template 174 | Kokkos::View createView( const std::string &label, 175 | const IndexSpace<1> &index_space ) 176 | { 177 | return Kokkos::View( 178 | Kokkos::ViewAllocateWithoutInitializing( label ), 179 | index_space.extent( 0 ) ); 180 | } 181 | 182 | //---------------------------------------------------------------------------// 183 | /*! 184 | \brief Given an index space and a data pointer create an unmanaged view over 185 | the extent of that index space. 186 | 187 | Rank-1 specialization. 188 | */ 189 | template 190 | Kokkos::View 191 | createView( const IndexSpace<1> &index_space, Scalar *data ) 192 | { 193 | return Kokkos::View( 194 | data, index_space.extent( 0 ) ); 195 | } 196 | 197 | //---------------------------------------------------------------------------// 198 | /*! 199 | \brief Given an index space create a view over the extent of that index 200 | space. 201 | 202 | Rank-2 specialization. 203 | */ 204 | template 205 | Kokkos::View 206 | createView( const std::string &label, const IndexSpace<2> &index_space ) 207 | { 208 | return Kokkos::View( 209 | Kokkos::ViewAllocateWithoutInitializing( label ), 210 | index_space.extent( 0 ), index_space.extent( 1 ) ); 211 | } 212 | 213 | //---------------------------------------------------------------------------// 214 | /*! 215 | \brief Given an index space and a data pointer create an unmanaged view over 216 | the extent of that index space. 217 | 218 | Rank-2 specialization. 219 | */ 220 | template 221 | Kokkos::View 222 | createView( const IndexSpace<2> &index_space, Scalar *data ) 223 | { 224 | return Kokkos::View( 225 | data, index_space.extent( 0 ), index_space.extent( 1 ) ); 226 | } 227 | 228 | //---------------------------------------------------------------------------// 229 | /*! 230 | \brief Given an index space create a view over the extent of that index 231 | space. 232 | 233 | Rank-3 specialization. 234 | */ 235 | template 236 | Kokkos::View 237 | createView( const std::string &label, const IndexSpace<3> &index_space ) 238 | { 239 | return Kokkos::View( 240 | Kokkos::ViewAllocateWithoutInitializing( label ), 241 | index_space.extent( 0 ), index_space.extent( 1 ), 242 | index_space.extent( 2 ) ); 243 | } 244 | 245 | //---------------------------------------------------------------------------// 246 | /*! 247 | \brief Given an index space and a data pointer create an unmanaged view over 248 | the extent of that index space. 249 | 250 | Rank-3 specialization. 251 | */ 252 | template 253 | Kokkos::View 254 | createView( const IndexSpace<3> &index_space, Scalar *data ) 255 | { 256 | return Kokkos::View( 257 | data, index_space.extent( 0 ), index_space.extent( 1 ), 258 | index_space.extent( 2 ) ); 259 | } 260 | 261 | //---------------------------------------------------------------------------// 262 | /*! 263 | \brief Given an index space create a view over the extent of that index 264 | space. 265 | 266 | Rank-4 specialization. 267 | */ 268 | template 269 | Kokkos::View 270 | createView( const std::string &label, const IndexSpace<4> &index_space ) 271 | { 272 | return Kokkos::View( 273 | Kokkos::ViewAllocateWithoutInitializing( label ), 274 | index_space.extent( 0 ), index_space.extent( 1 ), 275 | index_space.extent( 2 ), index_space.extent( 3 ) ); 276 | } 277 | 278 | //---------------------------------------------------------------------------// 279 | /*! 280 | \brief Given an index space and a data pointer create an unmanaged view over 281 | the extent of that index space. 282 | 283 | Rank-4 specialization. 284 | */ 285 | template 286 | Kokkos::View 287 | createView( const IndexSpace<4> &index_space, Scalar *data ) 288 | { 289 | return Kokkos::View( 290 | data, index_space.extent( 0 ), index_space.extent( 1 ), 291 | index_space.extent( 2 ), index_space.extent( 3 ) ); 292 | } 293 | 294 | //---------------------------------------------------------------------------// 295 | /*! 296 | \brief Given a view create a subview over the given index space. 297 | 298 | Rank-1 specialization. 299 | */ 300 | template 301 | auto createSubview( const ViewType &view, const IndexSpace<1> &index_space ) 302 | -> decltype( Kokkos::subview( view, index_space.range( 0 ) ) ) 303 | { 304 | static_assert( 1 == ViewType::Rank, "Incorrect view rank" ); 305 | return Kokkos::subview( view, index_space.range( 0 ) ); 306 | } 307 | 308 | //---------------------------------------------------------------------------// 309 | /*! 310 | \brief Given a view create a subview over the given index space. 311 | 312 | Rank-2 specialization. 313 | */ 314 | template 315 | auto createSubview( const ViewType &view, const IndexSpace<2> &index_space ) 316 | -> decltype( Kokkos::subview( view, index_space.range( 0 ), 317 | index_space.range( 1 ) ) ) 318 | { 319 | static_assert( 2 == ViewType::Rank, "Incorrect view rank" ); 320 | return Kokkos::subview( view, index_space.range( 0 ), 321 | index_space.range( 1 ) ); 322 | } 323 | 324 | //---------------------------------------------------------------------------// 325 | /*! 326 | \brief Given a view create a subview over the given index space. 327 | 328 | Rank-3 specialization. 329 | */ 330 | template 331 | auto createSubview( const ViewType &view, const IndexSpace<3> &index_space ) 332 | -> decltype( Kokkos::subview( view, index_space.range( 0 ), 333 | index_space.range( 1 ), 334 | index_space.range( 2 ) ) ) 335 | { 336 | static_assert( 3 == ViewType::Rank, "Incorrect view rank" ); 337 | return Kokkos::subview( view, index_space.range( 0 ), 338 | index_space.range( 1 ), index_space.range( 2 ) ); 339 | } 340 | 341 | //---------------------------------------------------------------------------// 342 | /*! 343 | \brief Given a view create a subview over the given index space. 344 | 345 | Rank-4 specialization. 346 | */ 347 | template 348 | auto createSubview( const ViewType &view, const IndexSpace<4> &index_space ) 349 | -> decltype( Kokkos::subview( view, index_space.range( 0 ), 350 | index_space.range( 1 ), 351 | index_space.range( 2 ), 352 | index_space.range( 3 ) ) ) 353 | { 354 | static_assert( 4 == ViewType::Rank, "Incorrect view rank" ); 355 | return Kokkos::subview( view, index_space.range( 0 ), 356 | index_space.range( 1 ), index_space.range( 2 ), 357 | index_space.range( 3 ) ); 358 | } 359 | 360 | //---------------------------------------------------------------------------// 361 | // Given an N-dimensional index space append an additional dimension with the 362 | // given size. 363 | template 364 | IndexSpace appendDimension( const IndexSpace &index_space, 365 | const long size ) 366 | { 367 | std::array min; 368 | for ( int d = 0; d < N; ++d ) 369 | min[d] = index_space.min( d ); 370 | min[N] = 0; 371 | 372 | std::array max; 373 | for ( int d = 0; d < N; ++d ) 374 | max[d] = index_space.max( d ); 375 | max[N] = size; 376 | 377 | return IndexSpace( min, max ); 378 | } 379 | 380 | //---------------------------------------------------------------------------// 381 | // Given an N-dimensional index space append an additional dimension with the 382 | // given range. 383 | template 384 | IndexSpace appendDimension( const IndexSpace &index_space, 385 | const long min, const long max ) 386 | { 387 | std::array range_min; 388 | for ( int d = 0; d < N; ++d ) 389 | range_min[d] = index_space.min( d ); 390 | range_min[N] = min; 391 | 392 | std::array range_max; 393 | for ( int d = 0; d < N; ++d ) 394 | range_max[d] = index_space.max( d ); 395 | range_max[N] = max; 396 | 397 | return IndexSpace( range_min, range_max ); 398 | } 399 | 400 | //---------------------------------------------------------------------------// 401 | 402 | } // end namespace Cajita 403 | 404 | #endif // end CAJITA_INDEXSPACE_HPP 405 | -------------------------------------------------------------------------------- /src/Cajita_LocalGrid.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_LOCALGRID_HPP 13 | #define CAJITA_LOCALGRID_HPP 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | namespace Cajita 23 | { 24 | //---------------------------------------------------------------------------// 25 | // Local logical grid. 26 | //---------------------------------------------------------------------------// 27 | template 28 | class LocalGrid 29 | { 30 | public: 31 | // Mesh type. 32 | using mesh_type = MeshType; 33 | 34 | /*! 35 | \brief Constructor. 36 | \param global_grid The global grid from which the local grid will be 37 | constructed. 38 | \param halo_cell_width The number of halo cells surrounding the locally 39 | owned cells. 40 | */ 41 | LocalGrid( const std::shared_ptr> &global_grid, 42 | const int halo_cell_width ); 43 | 44 | // Get the global grid that owns the local grid. 45 | const GlobalGrid &globalGrid() const; 46 | 47 | // Get the number of cells in the halo. 48 | int haloCellWidth() const; 49 | 50 | // Given the relative offsets of a neighbor rank relative to this local 51 | // grid's indices get the of the neighbor. If the neighbor rank is out of 52 | // bounds return -1. Note that in the case of periodic boundaries out of 53 | // bounds indices are allowed as the indices will be wrapped around the 54 | // periodic boundary. 55 | int neighborRank( const int off_i, const int off_j, const int off_k ) const; 56 | 57 | /* 58 | Get the index space of the local grid. 59 | 60 | Interface has the same structure as: 61 | 62 | template 63 | IndexSpace<3> 64 | indexSpace( DecompositionTag, EntityType, IndexType ) const; 65 | */ 66 | 67 | IndexSpace<3> indexSpace( Own, Cell, Local ) const; 68 | IndexSpace<3> indexSpace( Ghost, Cell, Local ) const; 69 | IndexSpace<3> indexSpace( Own, Cell, Global ) const; 70 | IndexSpace<3> indexSpace( Ghost, Cell, Global ) const; 71 | 72 | IndexSpace<3> indexSpace( Own, Node, Local ) const; 73 | IndexSpace<3> indexSpace( Ghost, Node, Local ) const; 74 | IndexSpace<3> indexSpace( Own, Node, Global ) const; 75 | IndexSpace<3> indexSpace( Ghost, Node, Global ) const; 76 | 77 | IndexSpace<3> indexSpace( Own, Face, Local ) const; 78 | IndexSpace<3> indexSpace( Ghost, Face, Local ) const; 79 | IndexSpace<3> indexSpace( Own, Face, Global ) const; 80 | IndexSpace<3> indexSpace( Ghost, Face, Global ) const; 81 | 82 | IndexSpace<3> indexSpace( Own, Face, Local ) const; 83 | IndexSpace<3> indexSpace( Ghost, Face, Local ) const; 84 | IndexSpace<3> indexSpace( Own, Face, Global ) const; 85 | IndexSpace<3> indexSpace( Ghost, Face, Global ) const; 86 | 87 | IndexSpace<3> indexSpace( Own, Face, Local ) const; 88 | IndexSpace<3> indexSpace( Ghost, Face, Local ) const; 89 | IndexSpace<3> indexSpace( Own, Face, Global ) const; 90 | IndexSpace<3> indexSpace( Ghost, Face, Global ) const; 91 | 92 | IndexSpace<3> indexSpace( Own, Edge, Local ) const; 93 | IndexSpace<3> indexSpace( Ghost, Edge, Local ) const; 94 | IndexSpace<3> indexSpace( Own, Edge, Global ) const; 95 | IndexSpace<3> indexSpace( Ghost, Edge, Global ) const; 96 | 97 | IndexSpace<3> indexSpace( Own, Edge, Local ) const; 98 | IndexSpace<3> indexSpace( Ghost, Edge, Local ) const; 99 | IndexSpace<3> indexSpace( Own, Edge, Global ) const; 100 | IndexSpace<3> indexSpace( Ghost, Edge, Global ) const; 101 | 102 | IndexSpace<3> indexSpace( Own, Edge, Local ) const; 103 | IndexSpace<3> indexSpace( Ghost, Edge, Local ) const; 104 | IndexSpace<3> indexSpace( Own, Edge, Global ) const; 105 | IndexSpace<3> indexSpace( Ghost, Edge, Global ) const; 106 | 107 | /* 108 | Given a relative set of indices of a neighbor get the set of local 109 | entity indices shared with that neighbor in the given 110 | decomposition. Optionally provide a halo width for the shared 111 | space. This halo width must be less than or equal to the halo width of 112 | the local grid. The default behavior is to use the halo width of the 113 | local grid. 114 | 115 | Interface has the same structure as: 116 | 117 | template 118 | IndexSpace<3> sharedIndexSpace( DecompositionTag, EntityType, 119 | const int off_i, const int off_j, 120 | const int off_k, 121 | const int halo_width = -1 ) const; 122 | */ 123 | 124 | IndexSpace<3> sharedIndexSpace( Own, Cell, const int off_i, const int off_j, 125 | const int off_k, 126 | const int halo_width = -1 ) const; 127 | 128 | IndexSpace<3> sharedIndexSpace( Ghost, Cell, const int off_i, 129 | const int off_j, const int off_k, 130 | const int halo_width = -1 ) const; 131 | 132 | IndexSpace<3> sharedIndexSpace( Own, Node, const int off_i, const int off_j, 133 | const int off_k, 134 | const int halo_width = -1 ) const; 135 | 136 | IndexSpace<3> sharedIndexSpace( Ghost, Node, const int off_i, 137 | const int off_j, const int off_k, 138 | const int halo_width = -1 ) const; 139 | 140 | IndexSpace<3> sharedIndexSpace( Own, Face, const int off_i, 141 | const int off_j, const int off_k, 142 | const int halo_width = -1 ) const; 143 | 144 | IndexSpace<3> sharedIndexSpace( Ghost, Face, const int off_i, 145 | const int off_j, const int off_k, 146 | const int halo_width = -1 ) const; 147 | 148 | IndexSpace<3> sharedIndexSpace( Own, Face, const int off_i, 149 | const int off_j, const int off_k, 150 | const int halo_width = -1 ) const; 151 | 152 | IndexSpace<3> sharedIndexSpace( Ghost, Face, const int off_i, 153 | const int off_j, const int off_k, 154 | const int halo_width = -1 ) const; 155 | 156 | IndexSpace<3> sharedIndexSpace( Own, Face, const int off_i, 157 | const int off_j, const int off_k, 158 | const int halo_width = -1 ) const; 159 | 160 | IndexSpace<3> sharedIndexSpace( Ghost, Face, const int off_i, 161 | const int off_j, const int off_k, 162 | const int halo_width = -1 ) const; 163 | 164 | IndexSpace<3> sharedIndexSpace( Own, Edge, const int off_i, 165 | const int off_j, const int off_k, 166 | const int halo_width = -1 ) const; 167 | 168 | IndexSpace<3> sharedIndexSpace( Ghost, Edge, const int off_i, 169 | const int off_j, const int off_k, 170 | const int halo_width = -1 ) const; 171 | 172 | IndexSpace<3> sharedIndexSpace( Own, Edge, const int off_i, 173 | const int off_j, const int off_k, 174 | const int halo_width = -1 ) const; 175 | 176 | IndexSpace<3> sharedIndexSpace( Ghost, Edge, const int off_i, 177 | const int off_j, const int off_k, 178 | const int halo_width = -1 ) const; 179 | 180 | IndexSpace<3> sharedIndexSpace( Own, Edge, const int off_i, 181 | const int off_j, const int off_k, 182 | const int halo_width = -1 ) const; 183 | 184 | IndexSpace<3> sharedIndexSpace( Ghost, Edge, const int off_i, 185 | const int off_j, const int off_k, 186 | const int halo_width = -1 ) const; 187 | 188 | private: 189 | // Get the global index space of the local grid. 190 | template 191 | IndexSpace<3> globalIndexSpace( Own, EntityType ) const; 192 | template 193 | IndexSpace<3> globalIndexSpace( Ghost, EntityType ) const; 194 | 195 | // Get the face index space of the local grid. 196 | template 197 | IndexSpace<3> faceIndexSpace( Own, Face, Local ) const; 198 | template 199 | IndexSpace<3> faceIndexSpace( Own, Face, Global ) const; 200 | template 201 | IndexSpace<3> faceIndexSpace( Ghost, Face, Local ) const; 202 | template 203 | IndexSpace<3> faceIndexSpace( Ghost, Face, Global ) const; 204 | 205 | // Given a relative set of indices of a neighbor get the set of local 206 | // face indices shared with that neighbor in the given decomposition. 207 | template 208 | IndexSpace<3> faceSharedIndexSpace( Own, Face, const int off_, 209 | const int off_j, const int off_k, 210 | const int halo_width ) const; 211 | template 212 | IndexSpace<3> faceSharedIndexSpace( Ghost, Face, const int off_i, 213 | const int off_j, const int off_k, 214 | const int halo_width ) const; 215 | 216 | // Get the edge index space of the local grid. 217 | template 218 | IndexSpace<3> edgeIndexSpace( Own, Edge, Local ) const; 219 | template 220 | IndexSpace<3> edgeIndexSpace( Own, Edge, Global ) const; 221 | template 222 | IndexSpace<3> edgeIndexSpace( Ghost, Edge, Local ) const; 223 | template 224 | IndexSpace<3> edgeIndexSpace( Ghost, Edge, Global ) const; 225 | 226 | // Given a relative set of indices of a neighbor get the set of local 227 | // edge indices shared with that neighbor in the given decomposition. 228 | template 229 | IndexSpace<3> edgeSharedIndexSpace( Own, Edge, const int off_, 230 | const int off_j, const int off_k, 231 | const int halo_width ) const; 232 | template 233 | IndexSpace<3> edgeSharedIndexSpace( Ghost, Edge, const int off_i, 234 | const int off_j, const int off_k, 235 | const int halo_width ) const; 236 | 237 | private: 238 | std::shared_ptr> _global_grid; 239 | int _halo_cell_width; 240 | }; 241 | 242 | //---------------------------------------------------------------------------// 243 | // Creation function. 244 | //---------------------------------------------------------------------------// 245 | /*! 246 | \brief Create a local grid. 247 | \param global_grid The global grid from which the local grid will be 248 | constructed. 249 | \param halo_cell_width The number of halo cells surrounding the locally 250 | owned cells. 251 | */ 252 | template 253 | std::shared_ptr> 254 | createLocalGrid( const std::shared_ptr> &global_grid, 255 | const int halo_cell_width ) 256 | { 257 | return std::make_shared>( global_grid, 258 | halo_cell_width ); 259 | } 260 | 261 | //---------------------------------------------------------------------------// 262 | 263 | } // end namespace Cajita 264 | 265 | #endif // end CAJITA_LOCALGRID_HPP 266 | -------------------------------------------------------------------------------- /src/Cajita_ManualPartitioner.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | 14 | namespace Cajita 15 | { 16 | //---------------------------------------------------------------------------// 17 | ManualPartitioner::ManualPartitioner( const std::array &ranks_per_dim ) 18 | : _ranks_per_dim( ranks_per_dim ) 19 | { 20 | } 21 | 22 | //---------------------------------------------------------------------------// 23 | std::array 24 | ManualPartitioner::ranksPerDimension( MPI_Comm, 25 | const std::array & ) const 26 | { 27 | return _ranks_per_dim; 28 | } 29 | 30 | //---------------------------------------------------------------------------// 31 | 32 | } // end namespace Cajita 33 | -------------------------------------------------------------------------------- /src/Cajita_ManualPartitioner.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_MANUALPARTITIONER_HPP 13 | #define CAJITA_MANUALPARTITIONER_HPP 14 | 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | namespace Cajita 22 | { 23 | //---------------------------------------------------------------------------// 24 | class ManualPartitioner : public Partitioner 25 | { 26 | public: 27 | ManualPartitioner( const std::array &ranks_per_dim ); 28 | 29 | std::array ranksPerDimension( 30 | MPI_Comm comm, 31 | const std::array &global_cells_per_dim ) const override; 32 | 33 | private: 34 | std::array _ranks_per_dim; 35 | }; 36 | 37 | //---------------------------------------------------------------------------// 38 | 39 | } // end namespace Cajita 40 | 41 | #endif // end CAJITA_MANUALPARTITIONER_HPP 42 | -------------------------------------------------------------------------------- /src/Cajita_MpiTraits.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_MPITRAITS_HPP 13 | #define CAJITA_MPITRAITS_HPP 14 | 15 | #include 16 | 17 | #include 18 | 19 | namespace Cajita 20 | { 21 | 22 | //---------------------------------------------------------------------------// 23 | // Type traits 24 | template 25 | struct MpiTraits; 26 | 27 | template <> 28 | struct MpiTraits 29 | { 30 | static MPI_Datatype type() { return MPI_CHAR; } 31 | }; 32 | 33 | template <> 34 | struct MpiTraits 35 | { 36 | static MPI_Datatype type() { return MPI_INT; } 37 | }; 38 | 39 | template <> 40 | struct MpiTraits 41 | { 42 | static MPI_Datatype type() { return MPI_LONG; } 43 | }; 44 | 45 | template <> 46 | struct MpiTraits 47 | { 48 | static MPI_Datatype type() { return MPI_FLOAT; } 49 | }; 50 | 51 | template <> 52 | struct MpiTraits 53 | { 54 | static MPI_Datatype type() { return MPI_DOUBLE; } 55 | }; 56 | 57 | //---------------------------------------------------------------------------// 58 | 59 | } // end namespace Cajita 60 | 61 | #endif // end CAJITA_MPITRAITS_HPP 62 | -------------------------------------------------------------------------------- /src/Cajita_Partitioner.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_PARTITIONER_HPP 13 | #define CAJITA_PARTITIONER_HPP 14 | 15 | #include 16 | 17 | #include 18 | 19 | namespace Cajita 20 | { 21 | //---------------------------------------------------------------------------// 22 | class Partitioner 23 | { 24 | public: 25 | ~Partitioner() = default; 26 | 27 | /*! 28 | \brief Get the number of MPI ranks in each dimension of the grid. 29 | \param comm The communicator to use for the partitioning. 30 | \param global_cells_per_dim The number of global cells in each dimension. 31 | \return The number of MPI ranks in each dimension of the grid. 32 | */ 33 | virtual std::array ranksPerDimension( 34 | MPI_Comm comm, 35 | const std::array &global_cells_per_dim ) const = 0; 36 | }; 37 | 38 | //---------------------------------------------------------------------------// 39 | 40 | } // end namespace Cajita 41 | 42 | #endif // end CAJITA_PARTITIONER_HPP 43 | -------------------------------------------------------------------------------- /src/Cajita_Types.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_TYPES_HPP 13 | #define CAJITA_TYPES_HPP 14 | 15 | #include 16 | 17 | namespace Cajita 18 | { 19 | 20 | //---------------------------------------------------------------------------// 21 | // Logical dimension index. 22 | //---------------------------------------------------------------------------// 23 | struct Dim 24 | { 25 | enum Values 26 | { 27 | I = 0, 28 | J = 1, 29 | K = 2 30 | }; 31 | }; 32 | 33 | //---------------------------------------------------------------------------// 34 | // Entity type tags. 35 | //---------------------------------------------------------------------------// 36 | 37 | // Mesh cell tag. 38 | struct Cell 39 | { 40 | }; 41 | 42 | // Mesh node tag. 43 | struct Node 44 | { 45 | }; 46 | 47 | // Mesh face tags. 48 | template 49 | struct Face; 50 | 51 | // I-face tag. 52 | template <> 53 | struct Face 54 | { 55 | static constexpr int dim = Dim::I; 56 | }; 57 | 58 | // J-face tag. 59 | template <> 60 | struct Face 61 | { 62 | static constexpr int dim = Dim::J; 63 | }; 64 | 65 | // K-face tag. 66 | template <> 67 | struct Face 68 | { 69 | static constexpr int dim = Dim::K; 70 | }; 71 | 72 | // Mesh edge tags. 73 | template 74 | struct Edge; 75 | 76 | // I-edge tag. 77 | template <> 78 | struct Edge 79 | { 80 | static constexpr int dim = Dim::I; 81 | }; 82 | 83 | // J-edge tag. 84 | template <> 85 | struct Edge 86 | { 87 | static constexpr int dim = Dim::J; 88 | }; 89 | 90 | // K-edge tag. 91 | template <> 92 | struct Edge 93 | { 94 | static constexpr int dim = Dim::K; 95 | }; 96 | 97 | // Type checkers. 98 | template 99 | struct isCell : public std::false_type 100 | { 101 | }; 102 | 103 | template <> 104 | struct isCell : public std::true_type 105 | { 106 | }; 107 | 108 | template <> 109 | struct isCell : public std::true_type 110 | { 111 | }; 112 | 113 | template 114 | struct isNode : public std::false_type 115 | { 116 | }; 117 | 118 | template <> 119 | struct isNode : public std::true_type 120 | { 121 | }; 122 | 123 | template <> 124 | struct isNode : public std::true_type 125 | { 126 | }; 127 | 128 | template 129 | struct isFace : public std::false_type 130 | { 131 | }; 132 | 133 | template 134 | struct isFace> : public std::true_type 135 | { 136 | }; 137 | 138 | template 139 | struct isFace> : public std::true_type 140 | { 141 | }; 142 | 143 | template 144 | struct isEdge : public std::false_type 145 | { 146 | }; 147 | 148 | template 149 | struct isEdge> : public std::true_type 150 | { 151 | }; 152 | 153 | template 154 | struct isEdge> : public std::true_type 155 | { 156 | }; 157 | 158 | //---------------------------------------------------------------------------// 159 | // Decomposition tags. 160 | //---------------------------------------------------------------------------// 161 | 162 | // Owned decomposition tag. 163 | struct Own 164 | { 165 | }; 166 | 167 | // Ghosted decomposition tag. 168 | struct Ghost 169 | { 170 | }; 171 | 172 | //---------------------------------------------------------------------------// 173 | // Index type tags. 174 | //---------------------------------------------------------------------------// 175 | 176 | // Local index tag. 177 | struct Local 178 | { 179 | }; 180 | 181 | // Global index tag. 182 | struct Global 183 | { 184 | }; 185 | 186 | //---------------------------------------------------------------------------// 187 | // Mesh type tags. 188 | //---------------------------------------------------------------------------// 189 | 190 | // Uniform mesh tag. 191 | template 192 | struct UniformMesh 193 | { 194 | // Scalar type for mesh floating point operations. 195 | using scalar_type = Scalar; 196 | }; 197 | 198 | // Non-uniform mesh tag. 199 | template 200 | struct NonUniformMesh 201 | { 202 | // Scalar type for mesh floating point operations. 203 | using scalar_type = Scalar; 204 | }; 205 | 206 | // Type checker. 207 | template 208 | struct isMeshType : public std::false_type 209 | { 210 | }; 211 | 212 | template 213 | struct isMeshType> : public std::true_type 214 | { 215 | }; 216 | 217 | template 218 | struct isMeshType> : public std::true_type 219 | { 220 | }; 221 | 222 | template 223 | struct isMeshType> : public std::true_type 224 | { 225 | }; 226 | 227 | template 228 | struct isMeshType> : public std::true_type 229 | { 230 | }; 231 | 232 | // Uniform mesh checker. 233 | template 234 | struct isUniformMesh : public std::false_type 235 | { 236 | }; 237 | 238 | template 239 | struct isUniformMesh> : public std::true_type 240 | { 241 | }; 242 | 243 | template 244 | struct isUniformMesh> : public std::true_type 245 | { 246 | }; 247 | 248 | // Non-uniform mesh checker. 249 | template 250 | struct isNonUniformMesh : public std::false_type 251 | { 252 | }; 253 | 254 | template 255 | struct isNonUniformMesh> : public std::true_type 256 | { 257 | }; 258 | 259 | template 260 | struct isNonUniformMesh> : public std::true_type 261 | { 262 | }; 263 | 264 | //---------------------------------------------------------------------------// 265 | 266 | } // end namespace Cajita 267 | 268 | #endif // CAJITA_TYPES_HPP 269 | -------------------------------------------------------------------------------- /src/Cajita_UniformDimPartitioner.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | 14 | namespace Cajita 15 | { 16 | //---------------------------------------------------------------------------// 17 | std::array 18 | UniformDimPartitioner::ranksPerDimension( MPI_Comm comm, 19 | const std::array & ) const 20 | { 21 | int comm_size; 22 | MPI_Comm_size( comm, &comm_size ); 23 | 24 | std::array ranks_per_dim = {0, 0, 0}; 25 | MPI_Dims_create( comm_size, 3, ranks_per_dim.data() ); 26 | 27 | return ranks_per_dim; 28 | } 29 | 30 | //---------------------------------------------------------------------------// 31 | 32 | } // end namespace Cajita 33 | -------------------------------------------------------------------------------- /src/Cajita_UniformDimPartitioner.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_UNIFORMDIMPARTITIONER_HPP 13 | #define CAJITA_UNIFORMDIMPARTITIONER_HPP 14 | 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | namespace Cajita 22 | { 23 | //---------------------------------------------------------------------------// 24 | class UniformDimPartitioner : public Partitioner 25 | { 26 | public: 27 | std::array ranksPerDimension( 28 | MPI_Comm comm, 29 | const std::array &global_cells_per_dim ) const override; 30 | }; 31 | 32 | //---------------------------------------------------------------------------// 33 | 34 | } // end namespace Cajita 35 | 36 | #endif // end CAJITA_UNIFORMDIMPARTITIONER_HPP 37 | -------------------------------------------------------------------------------- /src/Cajita_Version.hpp.in: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_VERSION_HPP 13 | #define CAJITA_VERSION_HPP 14 | 15 | #include 16 | 17 | #include 18 | 19 | namespace Cajita 20 | { 21 | 22 | inline std::string version() { return "@CAJITA_VERSION_STRING@"; } 23 | 24 | inline std::string gitCommitHash() { return "@CAJITA_GIT_COMMIT_HASH@"; } 25 | 26 | } // namespace Cajita 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /unit_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(gtest_args --gtest_color=yes) 2 | 3 | set(CAJITA_SUPPORTED_DEVICES SERIAL OPENMP CUDA) 4 | 5 | macro(Cajita_add_tests) 6 | cmake_parse_arguments(CAJITA_UNIT_TEST "MPI" "" "NAMES" ${ARGN}) 7 | set(CAJITA_UNIT_TEST_MPIEXEC_NUMPROCS 1) 8 | foreach( _np 2 4 8 ) 9 | if(MPIEXEC_MAX_NUMPROCS GREATER_EQUAL ${_np}) 10 | list(APPEND CAJITA_UNIT_TEST_MPIEXEC_NUMPROCS ${_np}) 11 | endif() 12 | endforeach() 13 | set(CAJITA_UNIT_TEST_NUMTHREADS 1) 14 | foreach( _nt 2 4 8 ) 15 | if(MPIEXEC_MAX_NUMPROCS GREATER_EQUAL ${_nt}) 16 | list(APPEND CAJITA_UNIT_TEST_NUMTHREADS ${_nt}) 17 | endif() 18 | endforeach() 19 | set(CAJITA_UNIT_TEST_MAIN unit_test_main.cpp) 20 | foreach(_device ${CAJITA_SUPPORTED_DEVICES}) 21 | if(Kokkos_ENABLE_${_device}) 22 | set(_dir ${CMAKE_CURRENT_BINARY_DIR}/${_device}) 23 | file(MAKE_DIRECTORY ${_dir}) 24 | foreach(_test ${CAJITA_UNIT_TEST_NAMES}) 25 | set(_file ${_dir}/tst${_test}_${_device}.cpp) 26 | file(WRITE ${_file} "#include \n") 27 | file(APPEND ${_file} "#include \n") 28 | set(_target ${_test}_test_${_device}) 29 | add_executable(${_target} ${_file} ${CAJITA_UNIT_TEST_MAIN}) 30 | target_include_directories(${_target} PRIVATE ${_dir} ${CMAKE_CURRENT_SOURCE_DIR}) 31 | target_link_libraries(${_target} PRIVATE Cajita gtest) 32 | if(CAJITA_UNIT_TEST_MPI) 33 | foreach(_np ${CAJITA_UNIT_TEST_MPIEXEC_NUMPROCS}) 34 | add_test(NAME ${_target}_${_np} COMMAND 35 | ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${_np} ${MPIEXEC_PREFLAGS} 36 | ${_target} ${MPIEXEC_POSTFLAGS} ${gtest_args}) 37 | endforeach() 38 | else() 39 | if(_device STREQUAL OpenMP) 40 | foreach(_thread ${CAJITA_UNIT_TEST_NUMTHREADS}) 41 | add_test(NAME ${_target}_${_thread} COMMAND 42 | ${_target} ${gtest_args} --kokkos-threads=${_thread}) 43 | endforeach() 44 | else() 45 | add_test(NAME ${_target} COMMAND ${_target} ${gtest_args}) 46 | endif() 47 | endif() 48 | endforeach() 49 | endif() 50 | endforeach() 51 | endmacro() 52 | 53 | set(SERIAL_TESTS 54 | GlobalMesh 55 | IndexSpace 56 | Splines 57 | ) 58 | 59 | set(MPI_TESTS 60 | GlobalGrid 61 | LocalGrid 62 | LocalMesh 63 | Array 64 | Halo 65 | SplineEvaluation 66 | Interpolation 67 | BovWriter 68 | ) 69 | 70 | if(HYPRE_FOUND) 71 | list(APPEND MPI_TESTS 72 | HypreStructuredSolver 73 | ) 74 | endif() 75 | 76 | if(CAJITA_HAVE_HEFFTE) 77 | list(APPEND MPI_TESTS 78 | FastFourierTransform 79 | ) 80 | endif() 81 | 82 | Cajita_add_tests(NAMES ${SERIAL_TESTS}) 83 | 84 | Cajita_add_tests(MPI NAMES ${MPI_TESTS}) 85 | -------------------------------------------------------------------------------- /unit_test/TestCUDA_Category.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_TEST_CUDA_CATEGORY_HPP 13 | #define CAJITA_TEST_CUDA_CATEGORY_HPP 14 | 15 | #define TEST_CATEGORY cuda 16 | #define TEST_EXECSPACE Kokkos::Cuda 17 | #define TEST_MEMSPACE Kokkos::CudaSpace 18 | #define TEST_DEVICE Kokkos::Device 19 | 20 | #endif // end CAJITA_TEST_CUDA_CATEGORY_HPP 21 | -------------------------------------------------------------------------------- /unit_test/TestOPENMP_Category.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_TEST_OPENMP_CATEGORY_HPP 13 | #define CAJITA_TEST_OPENMP_CATEGORY_HPP 14 | 15 | #define TEST_CATEGORY openmp 16 | #define TEST_EXECSPACE Kokkos::OpenMP 17 | #define TEST_MEMSPACE Kokkos::HostSpace 18 | #define TEST_DEVICE Kokkos::Device 19 | 20 | #endif // end CAJITA_TEST_OPENMP_CATEGORY_HPP 21 | -------------------------------------------------------------------------------- /unit_test/TestSERIAL_Category.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #ifndef CAJITA_TEST_SERIAL_CATEGORY_HPP 13 | #define CAJITA_TEST_SERIAL_CATEGORY_HPP 14 | 15 | #define TEST_CATEGORY serial 16 | #define TEST_EXECSPACE Kokkos::Serial 17 | #define TEST_MEMSPACE Kokkos::HostSpace 18 | #define TEST_DEVICE Kokkos::Device 19 | 20 | #endif // end CAJITA_TEST_SERIAL_CATEGORY_HPP 21 | -------------------------------------------------------------------------------- /unit_test/tstArray.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | using namespace Cajita; 29 | 30 | namespace Test 31 | { 32 | 33 | //---------------------------------------------------------------------------// 34 | void layoutTest() 35 | { 36 | // Let MPI compute the partitioning for this test. 37 | UniformDimPartitioner partitioner; 38 | 39 | // Create the global . 40 | double cell_size = 0.23; 41 | std::array global_num_cell = { 101, 85, 99 }; 42 | std::array is_dim_periodic = {true,true,true}; 43 | std::array global_low_corner = { 1.2, 3.3, -2.8 }; 44 | std::array global_high_corner = 45 | { global_low_corner[0] + cell_size * global_num_cell[0], 46 | global_low_corner[1] + cell_size * global_num_cell[1], 47 | global_low_corner[2] + cell_size * global_num_cell[2] }; 48 | auto global_mesh = createUniformGlobalMesh( global_low_corner, 49 | global_high_corner, 50 | global_num_cell ); 51 | 52 | // Create the global grid. 53 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 54 | global_mesh, 55 | is_dim_periodic, 56 | partitioner ); 57 | 58 | // Create an array layout on the nodes. 59 | int halo_width = 2; 60 | int dofs_per_node = 4; 61 | auto node_layout = 62 | createArrayLayout( global_grid, halo_width, dofs_per_node, Node() ); 63 | 64 | // Check the owned index_space. 65 | auto array_node_owned_space = 66 | node_layout->indexSpace( Own(), Local() ); 67 | auto grid_node_owned_space = 68 | node_layout->localGrid()->indexSpace( Own(), Node(), Local() ); 69 | for ( int d = 0; d < 3; ++d ) 70 | { 71 | EXPECT_EQ( array_node_owned_space.min(d), 72 | grid_node_owned_space.min(d) ); 73 | EXPECT_EQ( array_node_owned_space.max(d), 74 | grid_node_owned_space.max(d) ); 75 | } 76 | EXPECT_EQ( array_node_owned_space.min(3), 0 ); 77 | EXPECT_EQ( array_node_owned_space.max(3), dofs_per_node ); 78 | 79 | // Check the ghosted index_space. 80 | auto array_node_ghosted_space = 81 | node_layout->indexSpace( Ghost(), Local() ); 82 | auto grid_node_ghosted_space = 83 | node_layout->localGrid()->indexSpace( Ghost(), Node(), Local() ); 84 | for ( int d = 0; d < 3; ++d ) 85 | { 86 | EXPECT_EQ( array_node_ghosted_space.min(d), 87 | grid_node_ghosted_space.min(d) ); 88 | EXPECT_EQ( array_node_ghosted_space.max(d), 89 | grid_node_ghosted_space.max(d) ); 90 | } 91 | EXPECT_EQ( array_node_ghosted_space.min(3), 0 ); 92 | EXPECT_EQ( array_node_ghosted_space.max(3), dofs_per_node ); 93 | 94 | // Check the shared owned index_space. 95 | auto array_node_shared_owned_space = 96 | node_layout->sharedIndexSpace(Own(),-1,0,1); 97 | auto grid_node_shared_owned_space = 98 | node_layout->localGrid()->sharedIndexSpace( Own(), Node(), -1, 0, 1 ); 99 | for ( int d = 0; d < 3; ++d ) 100 | { 101 | EXPECT_EQ( array_node_shared_owned_space.min(d), 102 | grid_node_shared_owned_space.min(d) ); 103 | EXPECT_EQ( array_node_shared_owned_space.max(d), 104 | grid_node_shared_owned_space.max(d) ); 105 | } 106 | EXPECT_EQ( array_node_shared_owned_space.min(3), 0 ); 107 | EXPECT_EQ( array_node_shared_owned_space.max(3), dofs_per_node ); 108 | 109 | // Check the shared ghosted index_space. 110 | auto array_node_shared_ghosted_space = 111 | node_layout->sharedIndexSpace(Ghost(),1,-1,0); 112 | auto grid_node_shared_ghosted_space = 113 | node_layout->localGrid()->sharedIndexSpace( Ghost(), Node(), 1, -1, 0 ); 114 | for ( int d = 0; d < 3; ++d ) 115 | { 116 | EXPECT_EQ( array_node_shared_ghosted_space.min(d), 117 | grid_node_shared_ghosted_space.min(d) ); 118 | EXPECT_EQ( array_node_shared_ghosted_space.max(d), 119 | grid_node_shared_ghosted_space.max(d) ); 120 | } 121 | EXPECT_EQ( array_node_shared_ghosted_space.min(3), 0 ); 122 | EXPECT_EQ( array_node_shared_ghosted_space.max(3), dofs_per_node ); 123 | 124 | // Create an array layout on the cells. 125 | int dofs_per_cell = 4; 126 | auto cell_layout = 127 | createArrayLayout( global_grid, halo_width, dofs_per_cell, Cell() ); 128 | 129 | // Check the owned index_space. 130 | auto array_cell_owned_space = 131 | cell_layout->indexSpace( Own(), Local() ); 132 | auto grid_cell_owned_space = 133 | cell_layout->localGrid()->indexSpace( Own(), Cell(), Local() ); 134 | for ( int d = 0; d < 3; ++d ) 135 | { 136 | EXPECT_EQ( array_cell_owned_space.min(d), 137 | grid_cell_owned_space.min(d) ); 138 | EXPECT_EQ( array_cell_owned_space.max(d), 139 | grid_cell_owned_space.max(d) ); 140 | } 141 | EXPECT_EQ( array_cell_owned_space.min(3), 0 ); 142 | EXPECT_EQ( array_cell_owned_space.max(3), dofs_per_cell ); 143 | 144 | // Check the ghosted index_space. 145 | auto array_cell_ghosted_space = 146 | cell_layout->indexSpace( Ghost(), Local() ); 147 | auto grid_cell_ghosted_space = 148 | cell_layout->localGrid()->indexSpace( Ghost(), Cell(), Local() ); 149 | for ( int d = 0; d < 3; ++d ) 150 | { 151 | EXPECT_EQ( array_cell_ghosted_space.min(d), 152 | grid_cell_ghosted_space.min(d) ); 153 | EXPECT_EQ( array_cell_ghosted_space.max(d), 154 | grid_cell_ghosted_space.max(d) ); 155 | } 156 | EXPECT_EQ( array_cell_ghosted_space.min(3), 0 ); 157 | EXPECT_EQ( array_cell_ghosted_space.max(3), dofs_per_cell ); 158 | 159 | // Check the shared owned index_space. 160 | auto array_cell_shared_owned_space = 161 | cell_layout->sharedIndexSpace(Own(),0,1,-1); 162 | auto grid_cell_shared_owned_space = 163 | cell_layout->localGrid()->sharedIndexSpace( Own(), Cell(), 0, 1, -1 ); 164 | for ( int d = 0; d < 3; ++d ) 165 | { 166 | EXPECT_EQ( array_cell_shared_owned_space.min(d), 167 | grid_cell_shared_owned_space.min(d) ); 168 | EXPECT_EQ( array_cell_shared_owned_space.max(d), 169 | grid_cell_shared_owned_space.max(d) ); 170 | } 171 | EXPECT_EQ( array_cell_shared_owned_space.min(3), 0 ); 172 | EXPECT_EQ( array_cell_shared_owned_space.max(3), dofs_per_cell ); 173 | 174 | // Check the shared ghosted index_space. 175 | auto array_cell_shared_ghosted_space = 176 | cell_layout->sharedIndexSpace(Ghost(),1,1,1); 177 | auto grid_cell_shared_ghosted_space = 178 | cell_layout->localGrid()->sharedIndexSpace( Ghost(), Cell(), 1, 1, 1 ); 179 | for ( int d = 0; d < 3; ++d ) 180 | { 181 | EXPECT_EQ( array_cell_shared_ghosted_space.min(d), 182 | grid_cell_shared_ghosted_space.min(d) ); 183 | EXPECT_EQ( array_cell_shared_ghosted_space.max(d), 184 | grid_cell_shared_ghosted_space.max(d) ); 185 | } 186 | EXPECT_EQ( array_cell_shared_ghosted_space.min(3), 0 ); 187 | EXPECT_EQ( array_cell_shared_ghosted_space.max(3), dofs_per_cell ); 188 | } 189 | 190 | //---------------------------------------------------------------------------// 191 | void arrayTest() 192 | { 193 | // Let MPI compute the partitioning for this test. 194 | UniformDimPartitioner partitioner; 195 | 196 | // Create the global mesh. 197 | double cell_size = 0.23; 198 | std::array global_num_cell = { 101, 85, 99 }; 199 | std::array is_dim_periodic = {true,true,true}; 200 | std::array global_low_corner = { 1.2, 3.3, -2.8 }; 201 | std::array global_high_corner = 202 | { global_low_corner[0] + cell_size * global_num_cell[0], 203 | global_low_corner[1] + cell_size * global_num_cell[1], 204 | global_low_corner[2] + cell_size * global_num_cell[2] }; 205 | auto global_mesh = createUniformGlobalMesh( global_low_corner, 206 | global_high_corner, 207 | global_num_cell ); 208 | 209 | // Create the global grid. 210 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 211 | global_mesh, 212 | is_dim_periodic, 213 | partitioner ); 214 | 215 | // Create an array layout on the cells. 216 | int halo_width = 2; 217 | int dofs_per_cell = 4; 218 | auto cell_layout = 219 | createArrayLayout( global_grid, halo_width, dofs_per_cell, Cell() ); 220 | 221 | // Create an array. 222 | std::string label( "test_array" ); 223 | auto array = createArray( label, cell_layout ); 224 | 225 | // Check the array. 226 | EXPECT_EQ( label, array->label() ); 227 | auto space = cell_layout->indexSpace( Ghost(), Local() ); 228 | auto view = array->view(); 229 | EXPECT_EQ( label, view.label() ); 230 | EXPECT_EQ( view.size(), space.size() ); 231 | for ( int i = 0; i < 4; ++i ) 232 | EXPECT_EQ( view.extent(i), space.extent(i) ); 233 | } 234 | 235 | //---------------------------------------------------------------------------// 236 | void arrayOpTest() 237 | { 238 | // Let MPI compute the partitioning for this test. 239 | UniformDimPartitioner partitioner; 240 | 241 | // Create the global mesh. 242 | double cell_size = 0.23; 243 | std::array global_num_cell = { 101, 85, 99 }; 244 | std::array is_dim_periodic = {true,true,true}; 245 | std::array global_low_corner = { 1.2, 3.3, -2.8 }; 246 | std::array global_high_corner = 247 | { global_low_corner[0] + cell_size * global_num_cell[0], 248 | global_low_corner[1] + cell_size * global_num_cell[1], 249 | global_low_corner[2] + cell_size * global_num_cell[2] }; 250 | auto global_mesh = createUniformGlobalMesh( global_low_corner, 251 | global_high_corner, 252 | global_num_cell ); 253 | 254 | // Create the global grid. 255 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 256 | global_mesh, 257 | is_dim_periodic, 258 | partitioner ); 259 | 260 | // Create an array layout on the cells. 261 | int halo_width = 2; 262 | int dofs_per_cell = 4; 263 | auto cell_layout = 264 | createArrayLayout( global_grid, halo_width, dofs_per_cell, Cell() ); 265 | 266 | // Create an array. 267 | std::string label( "test_array" ); 268 | auto array = createArray( label, cell_layout ); 269 | 270 | // Assign a value to the entire the array. 271 | ArrayOp::assign( *array, 2.0, Ghost() ); 272 | auto host_view = Kokkos::create_mirror_view_and_copy( 273 | Kokkos::HostSpace(), array->view() ); 274 | auto ghosted_space = array->layout()->indexSpace( Ghost(), Local() ); 275 | for ( long i = 0; i < ghosted_space.extent(Dim::I); ++i ) 276 | for ( long j = 0; j < ghosted_space.extent(Dim::J); ++j ) 277 | for ( long k = 0; k < ghosted_space.extent(Dim::K); ++k ) 278 | for ( long l = 0; l < ghosted_space.extent(3); ++l ) 279 | EXPECT_EQ( host_view(i,j,k,l), 2.0 ); 280 | 281 | // Scale the entire array with a single value. 282 | ArrayOp::scale( *array, 0.5, Ghost() ); 283 | Kokkos::deep_copy( host_view, array->view() ); 284 | for ( long i = 0; i < ghosted_space.extent(Dim::I); ++i ) 285 | for ( long j = 0; j < ghosted_space.extent(Dim::J); ++j ) 286 | for ( long k = 0; k < ghosted_space.extent(Dim::K); ++k ) 287 | for ( long l = 0; l < ghosted_space.extent(3); ++l ) 288 | EXPECT_EQ( host_view(i,j,k,l), 1.0 ); 289 | 290 | // Scale each array component by a different value. 291 | std::vector scales = { 2.3, 1.5, 8.9, -12.1 }; 292 | ArrayOp::scale( *array, scales, Ghost() ); 293 | Kokkos::deep_copy( host_view, array->view() ); 294 | for ( long i = 0; i < ghosted_space.extent(Dim::I); ++i ) 295 | for ( long j = 0; j < ghosted_space.extent(Dim::J); ++j ) 296 | for ( long k = 0; k < ghosted_space.extent(Dim::K); ++k ) 297 | for ( long l = 0; l < ghosted_space.extent(3); ++l ) 298 | EXPECT_EQ( host_view(i,j,k,l), scales[l] ); 299 | 300 | // Create another array and update. 301 | auto array_2 = createArray( label, cell_layout ); 302 | ArrayOp::assign( *array_2, 0.5, Ghost() ); 303 | ArrayOp::update( *array, 3.0, *array_2, 2.0, Ghost() ); 304 | Kokkos::deep_copy( host_view, array->view() ); 305 | for ( long i = 0; i < ghosted_space.extent(Dim::I); ++i ) 306 | for ( long j = 0; j < ghosted_space.extent(Dim::J); ++j ) 307 | for ( long k = 0; k < ghosted_space.extent(Dim::K); ++k ) 308 | for ( long l = 0; l < ghosted_space.extent(3); ++l ) 309 | EXPECT_EQ( host_view(i,j,k,l), 3.0*scales[l]+1.0 ); 310 | 311 | // Check the subarray. 312 | auto subarray = createSubarray( *array, 2, 4 ); 313 | auto sub_ghosted_space = subarray->layout()->indexSpace( Ghost(), Local() ); 314 | EXPECT_EQ( sub_ghosted_space.rank(), 4 ); 315 | EXPECT_EQ( sub_ghosted_space.extent(Dim::I), ghosted_space.extent(Dim::I) ); 316 | EXPECT_EQ( sub_ghosted_space.extent(Dim::J), ghosted_space.extent(Dim::J) ); 317 | EXPECT_EQ( sub_ghosted_space.extent(Dim::K), ghosted_space.extent(Dim::K) ); 318 | EXPECT_EQ( sub_ghosted_space.extent(3), 2 ); 319 | auto host_subview = Kokkos::create_mirror_view_and_copy( 320 | Kokkos::HostSpace(), subarray->view() ); 321 | for ( long i = 0; i < sub_ghosted_space.extent(Dim::I); ++i ) 322 | for ( long j = 0; j < sub_ghosted_space.extent(Dim::J); ++j ) 323 | for ( long k = 0; k < sub_ghosted_space.extent(Dim::K); ++k ) 324 | for ( long l = 0; l < sub_ghosted_space.extent(3); ++l ) 325 | EXPECT_EQ( host_subview(i,j,k,l), 3.0*scales[l+2]+1.0 ); 326 | 327 | // Compute the dot product of the two arrays. 328 | std::vector dots( dofs_per_cell ); 329 | ArrayOp::dot( *array, *array_2, dots ); 330 | int total_num_node = global_grid->globalNumEntity(Node(),Dim::I) * 331 | global_grid->globalNumEntity(Node(),Dim::J) * 332 | global_grid->globalNumEntity(Node(),Dim::K); 333 | for ( int n = 0; n < dofs_per_cell; ++n ) 334 | EXPECT_FLOAT_EQ( dots[n], (3.0*scales[n]+1.0)*0.5*total_num_node ); 335 | 336 | // Compute the two-norm of the array components 337 | std::vector norm_2( dofs_per_cell ); 338 | ArrayOp::norm2( *array, norm_2 ); 339 | for ( int n = 0; n < dofs_per_cell; ++n ) 340 | EXPECT_FLOAT_EQ( 341 | norm_2[n], 342 | std::sqrt( std::pow(3.0*scales[n]+1.0,2.0)*total_num_node) ); 343 | 344 | // Compute the one-norm of the array components 345 | std::vector norm_1( dofs_per_cell ); 346 | ArrayOp::norm1( *array, norm_1 ); 347 | for ( int n = 0; n < dofs_per_cell; ++n ) 348 | EXPECT_FLOAT_EQ( norm_1[n], fabs(3.0*scales[n]+1.0)*total_num_node ); 349 | 350 | // Compute the infinity-norm of the array components 351 | std::vector large_vals = { -1939304932.2, 352 | 20399994.532, 353 | 9098201010.114, 354 | -89877402343.99 }; 355 | for ( int n = 0; n < dofs_per_cell; ++n ) 356 | host_view( 4, 4, 4, n ) = large_vals[n]; 357 | Kokkos::deep_copy( array->view(), host_view ); 358 | std::vector norm_inf( dofs_per_cell ); 359 | ArrayOp::normInf( *array, norm_inf ); 360 | for ( int n = 0; n < dofs_per_cell; ++n ) 361 | EXPECT_FLOAT_EQ( norm_inf[n], fabs(large_vals[n]) ); 362 | 363 | // Check the copy. 364 | ArrayOp::copy( *array, *array_2, Own() ); 365 | Kokkos::deep_copy( host_view, array->view() ); 366 | auto owned_space = array->layout()->indexSpace( Own(), Local() ); 367 | for ( long i = owned_space.min(Dim::I); i < owned_space.max(Dim::I); ++i ) 368 | for ( long j = owned_space.min(Dim::J); j < owned_space.max(Dim::J); ++j ) 369 | for ( long k = owned_space.min(Dim::K); k < owned_space.max(Dim::K); ++k ) 370 | for ( long l = 0; l < owned_space.extent(3); ++l ) 371 | EXPECT_EQ( host_view(i,j,k,l), 0.5 ); 372 | 373 | // Now make a clone and copy. 374 | auto array_3 = ArrayOp::clone( *array ); 375 | ArrayOp::copy( *array_3, *array, Own() ); 376 | Kokkos::deep_copy( host_view, array_3->view() ); 377 | for ( long i = owned_space.min(Dim::I); i < owned_space.max(Dim::I); ++i ) 378 | for ( long j = owned_space.min(Dim::J); j < owned_space.max(Dim::J); ++j ) 379 | for ( long k = owned_space.min(Dim::K); k < owned_space.max(Dim::K); ++k ) 380 | for ( long l = 0; l < owned_space.extent(3); ++l ) 381 | EXPECT_EQ( host_view(i,j,k,l), 0.5 ); 382 | 383 | // Test the fused clone copy. 384 | auto array_4 = ArrayOp::cloneCopy( *array, Own() ); 385 | Kokkos::deep_copy( host_view, array_4->view() ); 386 | for ( long i = owned_space.min(Dim::I); i < owned_space.max(Dim::I); ++i ) 387 | for ( long j = owned_space.min(Dim::J); j < owned_space.max(Dim::J); ++j ) 388 | for ( long k = owned_space.min(Dim::K); k < owned_space.max(Dim::K); ++k ) 389 | for ( long l = 0; l < owned_space.extent(3); ++l ) 390 | EXPECT_EQ( host_view(i,j,k,l), 0.5 ); 391 | } 392 | 393 | //---------------------------------------------------------------------------// 394 | // RUN TESTS 395 | //---------------------------------------------------------------------------// 396 | TEST( array, array_test ) 397 | { 398 | layoutTest(); 399 | arrayTest(); 400 | arrayOpTest(); 401 | } 402 | 403 | //---------------------------------------------------------------------------// 404 | 405 | } // end namespace Test 406 | -------------------------------------------------------------------------------- /unit_test/tstBovWriter.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | using namespace Cajita; 32 | 33 | namespace Test 34 | { 35 | //---------------------------------------------------------------------------// 36 | void writeTest() 37 | { 38 | // Create the global mesh. 39 | UniformDimPartitioner partitioner; 40 | double cell_size = 0.23; 41 | std::array global_num_cell = { 22, 19, 21 }; 42 | std::array global_low_corner = { 1.2, 3.3, -2.8 }; 43 | std::array global_high_corner = 44 | { global_low_corner[0] + cell_size * global_num_cell[0], 45 | global_low_corner[1] + cell_size * global_num_cell[1], 46 | global_low_corner[2] + cell_size * global_num_cell[2] }; 47 | std::array is_dim_periodic = {true,true,true}; 48 | auto global_mesh = createUniformGlobalMesh( global_low_corner, 49 | global_high_corner, 50 | global_num_cell ); 51 | 52 | // Create the global grid. 53 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 54 | global_mesh, 55 | is_dim_periodic, 56 | partitioner ); 57 | 58 | 59 | // Get the global ijk offsets. 60 | auto off_i = global_grid->globalOffset( Dim::I ); 61 | auto off_j = global_grid->globalOffset( Dim::J ); 62 | auto off_k = global_grid->globalOffset( Dim::K ); 63 | 64 | // Create a scalar cell field and fill it with data. 65 | auto cell_layout = createArrayLayout( global_grid, 0, 1, Cell() ); 66 | auto cell_field = 67 | createArray( "cell_field", cell_layout ); 68 | auto cell_data = cell_field->view(); 69 | Kokkos::parallel_for( 70 | "fill_cell_field", 71 | createExecutionPolicy( 72 | cell_layout->localGrid()->indexSpace(Own(),Cell(),Local()), 73 | TEST_EXECSPACE() ), 74 | KOKKOS_LAMBDA( const int i, const int j, const int k ){ 75 | cell_data( i, j, k, 0 ) = i + off_i + j + off_j + k + off_k; 76 | } ); 77 | 78 | // Create a vector node field and fill it with data. 79 | auto node_layout = createArrayLayout( global_grid, 0, 3, Node() ); 80 | auto node_field = 81 | createArray( "node_field", node_layout ); 82 | auto node_data = node_field->view(); 83 | Kokkos::parallel_for( 84 | "fill_node_field", 85 | createExecutionPolicy( 86 | node_layout->localGrid()->indexSpace(Own(),Node(),Local()), 87 | TEST_EXECSPACE() ), 88 | KOKKOS_LAMBDA( const int i, const int j, const int k ){ 89 | node_data( i, j, k, Dim::I ) = i + off_i; 90 | node_data( i, j, k, Dim::J ) = j + off_j; 91 | node_data( i, j, k, Dim::K ) = k + off_k; 92 | } ); 93 | 94 | // Gather the node data. 95 | auto node_halo = createHalo( *node_field, FullHaloPattern() ); 96 | node_halo->gather( *node_field ); 97 | 98 | // Write the fields to a file. 99 | BovWriter::writeTimeStep( 302, 3.43, *cell_field ); 100 | BovWriter::writeTimeStep( 1972, 12.457, *node_field ); 101 | 102 | // Read the data back in on rank 0 and make sure it is OK. 103 | int rank; 104 | MPI_Comm_rank( MPI_COMM_WORLD, &rank ); 105 | if ( 0 == rank ) 106 | { 107 | // Open the cell file. 108 | std::fstream cell_data_file; 109 | cell_data_file.open( "grid_cell_field_000302.dat", 110 | std::fstream::in | std::fstream::binary ); 111 | 112 | // The cell file data is ordered KJI 113 | double cell_value; 114 | int cell_id = 0; 115 | for ( int k = 0; k < global_grid->globalNumEntity(Cell(),Dim::K); ++k ) 116 | for ( int j = 0; j < global_grid->globalNumEntity(Cell(),Dim::J); ++j ) 117 | for ( int i = 0; i < global_grid->globalNumEntity(Cell(),Dim::I); ++i ) 118 | { 119 | cell_data_file.seekg( cell_id * sizeof(double) ); 120 | cell_data_file.read( (char*) &cell_value, sizeof(double) ); 121 | EXPECT_EQ( cell_value, i + j + k ); 122 | ++cell_id; 123 | } 124 | 125 | // Close the cell file. 126 | cell_data_file.close(); 127 | 128 | // Open the node file. 129 | std::fstream node_data_file; 130 | node_data_file.open( "grid_node_field_001972.dat", 131 | std::fstream::in | std::fstream::binary ); 132 | 133 | // The node file data is ordered KJI 134 | int node_value; 135 | int node_id = 0; 136 | for ( int k = 0; k < global_grid->globalNumEntity(Cell(),Dim::K)+1; ++k ) 137 | for ( int j = 0; j < global_grid->globalNumEntity(Cell(),Dim::J)+1; ++j ) 138 | for ( int i = 0; i < global_grid->globalNumEntity(Cell(),Dim::I)+1; ++i ) 139 | { 140 | auto ival = 141 | ( i == global_grid->globalNumEntity(Cell(),Dim::I) ) ? 0 : i; 142 | node_data_file.seekg( node_id * sizeof(int) ); 143 | node_data_file.read( (char*) &node_value, sizeof(int) ); 144 | EXPECT_EQ( node_value, ival ); 145 | ++node_id; 146 | 147 | auto jval = 148 | ( j == global_grid->globalNumEntity(Cell(),Dim::J) ) ? 0 : j; 149 | node_data_file.seekg( node_id * sizeof(int) ); 150 | node_data_file.read( (char*) &node_value, sizeof(int) ); 151 | EXPECT_EQ( node_value, jval ); 152 | ++node_id; 153 | 154 | auto kval = 155 | ( k == global_grid->globalNumEntity(Cell(),Dim::K) ) ? 0 : k; 156 | node_data_file.seekg( node_id * sizeof(int) ); 157 | node_data_file.read( (char*) &node_value, sizeof(int) ); 158 | EXPECT_EQ( node_value, kval ); 159 | ++node_id; 160 | } 161 | 162 | // Close the node file. 163 | node_data_file.close(); 164 | } 165 | } 166 | 167 | //---------------------------------------------------------------------------// 168 | // RUN TESTS 169 | //---------------------------------------------------------------------------// 170 | TEST( TEST_CATEGORY, write_test ) 171 | { 172 | writeTest(); 173 | } 174 | 175 | //---------------------------------------------------------------------------// 176 | 177 | } // end namespace Test 178 | -------------------------------------------------------------------------------- /unit_test/tstFastFourierTransform.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | using namespace Cajita; 30 | 31 | namespace Test 32 | { 33 | //---------------------------------------------------------------------------// 34 | void memoryTest() 35 | { 36 | auto mtype = Experimental::HeffteMemoryTraits::value; 37 | HEFFTE::Memory fft_mem; 38 | fft_mem.memory_type = mtype; 39 | int size = 12; 40 | int nbytes = size * sizeof(double); 41 | double* ptr = (double*) fft_mem.smalloc( nbytes, mtype ); 42 | EXPECT_NE( ptr, nullptr ); 43 | fft_mem.sfree( ptr, mtype ); 44 | } 45 | 46 | //---------------------------------------------------------------------------// 47 | void forwardReverseTest() 48 | { 49 | // Create the global mesh. 50 | double cell_size = 0.1; 51 | std::array is_dim_periodic = {true,true,true}; 52 | std::array global_low_corner = {-1.0, -2.0, -1.0 }; 53 | std::array global_high_corner = { 1.0, 1.0, 0.5 }; 54 | auto global_mesh = createUniformGlobalMesh( 55 | global_low_corner, global_high_corner, cell_size ); 56 | 57 | // Create the global grid. 58 | UniformDimPartitioner partitioner; 59 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 60 | global_mesh, 61 | is_dim_periodic, 62 | partitioner ); 63 | 64 | // Create a local grid. 65 | auto local_grid = createLocalGrid( global_grid, 0 ); 66 | auto owned_space = local_grid->indexSpace(Own(),Cell(),Local()); 67 | auto ghosted_space = local_grid->indexSpace(Ghost(),Cell(),Local()); 68 | 69 | // Create a random vector to transform.. 70 | auto vector_layout = createArrayLayout( local_grid, 1, Cell() ); 71 | auto lhs = createArray,TEST_DEVICE>( "lhs", vector_layout ); 72 | auto lhs_view = lhs->view(); 73 | auto lhs_host = createArray,typename decltype(lhs_view)::array_layout,Kokkos::HostSpace>( "lhs_host", vector_layout ); 74 | auto lhs_host_view = lhs_host->view(); 75 | uint64_t seed = global_grid->blockId() + ( 19383747 % (global_grid->blockId() + 1) ); 76 | using rnd_type = Kokkos::Random_XorShift64_Pool; 77 | rnd_type pool; 78 | pool.init( seed, ghosted_space.size() ); 79 | for ( int i = owned_space.min(Dim::I); 80 | i < owned_space.max(Dim::I); 81 | ++i ) 82 | for ( int j = owned_space.min(Dim::J); 83 | j < owned_space.max(Dim::J); 84 | ++j ) 85 | for ( int k = owned_space.min(Dim::K); 86 | k < owned_space.max(Dim::K); 87 | ++k ) 88 | { 89 | auto rand = pool.get_state( i + j + k ); 90 | lhs_host_view(i,j,k,0).real() = 91 | Kokkos::rand::draw( rand, 0.0, 1.0 ); 92 | lhs_host_view(i,j,k,0).imag() = 93 | Kokkos::rand::draw( rand, 0.0, 1.0 ); 94 | } 95 | 96 | // Copy to the device. 97 | Kokkos::deep_copy( lhs_view, lhs_host_view ); 98 | 99 | // Create an FFT 100 | auto fft = Experimental::createFastFourierTransform( 101 | *vector_layout, Experimental::FastFourierTransformParams{}.setCollectiveType( 2 ).setExchangeType( 0 ).setPackType( 2 ).setScalingType( 1 ) ); 102 | 103 | // Forward transform 104 | fft->forward( *lhs ); 105 | 106 | // Reverse transform 107 | fft->reverse( *lhs ); 108 | 109 | // Check the results. 110 | auto lhs_result = Kokkos::create_mirror_view_and_copy( 111 | Kokkos::HostSpace(), lhs->view() ); 112 | for ( int i = owned_space.min(Dim::I); 113 | i < owned_space.max(Dim::I); 114 | ++i ) 115 | for ( int j = owned_space.min(Dim::J); 116 | j < owned_space.max(Dim::J); 117 | ++j ) 118 | for ( int k = owned_space.min(Dim::K); 119 | k < owned_space.max(Dim::K); 120 | ++k ) 121 | { 122 | EXPECT_FLOAT_EQ( lhs_host_view(i,j,k,0).real(), lhs_result(i,j,k,0).real() ); 123 | EXPECT_FLOAT_EQ( lhs_host_view(i,j,k,0).imag(), lhs_result(i,j,k,0).imag() ); 124 | } 125 | } 126 | 127 | //---------------------------------------------------------------------------// 128 | // RUN TESTS 129 | //---------------------------------------------------------------------------// 130 | // NOTE: This test exposes the GPU FFT memory bug in HEFFTE. Re-enable this 131 | // when we enable GPU FFTs to test the bug. 132 | // TEST( fast_fourier_transform, memory_test ) 133 | // { 134 | // memoryTest(); 135 | // } 136 | 137 | //---------------------------------------------------------------------------// 138 | TEST( fast_fourier_transform, forward_reverse_test ) 139 | { 140 | forwardReverseTest(); 141 | } 142 | 143 | //---------------------------------------------------------------------------// 144 | 145 | } // end namespace Test 146 | -------------------------------------------------------------------------------- /unit_test/tstGlobalGrid.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | 23 | using namespace Cajita; 24 | 25 | namespace Test 26 | { 27 | 28 | //---------------------------------------------------------------------------// 29 | void gridTest( const std::array& is_dim_periodic ) 30 | { 31 | // Let MPI compute the partitioning for this test. 32 | UniformDimPartitioner partitioner; 33 | 34 | // Create the global mesh. 35 | double cell_size = 0.23; 36 | std::array global_num_cell = { 101, 85, 99 }; 37 | std::array global_low_corner = { 1.2, 3.3, -2.8 }; 38 | std::array global_high_corner = 39 | { global_low_corner[0] + cell_size * global_num_cell[0], 40 | global_low_corner[1] + cell_size * global_num_cell[1], 41 | global_low_corner[2] + cell_size * global_num_cell[2] }; 42 | auto global_mesh = createUniformGlobalMesh( global_low_corner, 43 | global_high_corner, 44 | global_num_cell ); 45 | 46 | // Create the global grid. 47 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 48 | global_mesh, 49 | is_dim_periodic, 50 | partitioner ); 51 | 52 | // Check the number of entities. 53 | for ( int d = 0; d < 3; ++d ) 54 | { 55 | EXPECT_EQ( global_num_cell[d], 56 | global_grid->globalNumEntity(Cell(),d) ); 57 | if ( is_dim_periodic[d] ) 58 | EXPECT_EQ( global_num_cell[d], 59 | global_grid->globalNumEntity(Node(),d) ); 60 | else 61 | EXPECT_EQ( global_num_cell[d] + 1, 62 | global_grid->globalNumEntity(Node(),d) ); 63 | 64 | } 65 | 66 | // Number of I faces 67 | if ( is_dim_periodic[Dim::I] ) 68 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::I ), 69 | global_num_cell[Dim::I] ); 70 | else 71 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::I ), 72 | global_num_cell[Dim::I] + 1 ); 73 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::J ), 74 | global_num_cell[Dim::J] ); 75 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::K ), 76 | global_num_cell[Dim::K] ); 77 | 78 | // Number of J faces. 79 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::I ), 80 | global_num_cell[Dim::I] ); 81 | if ( is_dim_periodic[Dim::I] ) 82 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::J ), 83 | global_num_cell[Dim::J] ); 84 | else 85 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::J ), 86 | global_num_cell[Dim::J] + 1 ); 87 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::K ), 88 | global_num_cell[Dim::K] ); 89 | 90 | // Number of K faces. 91 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::I ), 92 | global_num_cell[Dim::I] ); 93 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::J ), 94 | global_num_cell[Dim::J] ); 95 | if ( is_dim_periodic[Dim::I] ) 96 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::K ), 97 | global_num_cell[Dim::K] ); 98 | else 99 | EXPECT_EQ( global_grid->globalNumEntity( Face(), Dim::K ), 100 | global_num_cell[Dim::K] + 1 ); 101 | 102 | // Number of I edges 103 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::I ), 104 | global_num_cell[Dim::I] ); 105 | if ( is_dim_periodic[Dim::J] ) 106 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::J ), 107 | global_num_cell[Dim::J] ); 108 | else 109 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::J ), 110 | global_num_cell[Dim::J] + 1 ); 111 | if ( is_dim_periodic[Dim::K] ) 112 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::K ), 113 | global_num_cell[Dim::K] ); 114 | else 115 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::K ), 116 | global_num_cell[Dim::K] + 1 ); 117 | 118 | // Number of J edges 119 | if ( is_dim_periodic[Dim::I] ) 120 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::I ), 121 | global_num_cell[Dim::I] ); 122 | else 123 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::I ), 124 | global_num_cell[Dim::I] + 1 ); 125 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::J ), 126 | global_num_cell[Dim::J] ); 127 | if ( is_dim_periodic[Dim::K] ) 128 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::K ), 129 | global_num_cell[Dim::K] ); 130 | else 131 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::K ), 132 | global_num_cell[Dim::K] + 1 ); 133 | 134 | // Number of K edges 135 | if ( is_dim_periodic[Dim::I] ) 136 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::I ), 137 | global_num_cell[Dim::I] ); 138 | else 139 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::I ), 140 | global_num_cell[Dim::I] + 1 ); 141 | if ( is_dim_periodic[Dim::J] ) 142 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::J ), 143 | global_num_cell[Dim::J] ); 144 | else 145 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::J ), 146 | global_num_cell[Dim::J] + 1 ); 147 | EXPECT_EQ( global_grid->globalNumEntity( Edge(), Dim::K ), 148 | global_num_cell[Dim::K] ); 149 | 150 | // Check the partitioning. The grid communicator has a Cartesian topology. 151 | int comm_size; 152 | MPI_Comm_size( MPI_COMM_WORLD, &comm_size ); 153 | auto grid_comm = global_grid->comm(); 154 | int grid_comm_size; 155 | MPI_Comm_size( grid_comm, &grid_comm_size ); 156 | int grid_comm_rank; 157 | MPI_Comm_rank( grid_comm, &grid_comm_rank ); 158 | EXPECT_EQ( grid_comm_size, comm_size ); 159 | EXPECT_EQ( global_grid->totalNumBlock(), grid_comm_size ); 160 | EXPECT_EQ( global_grid->blockId(), grid_comm_rank ); 161 | 162 | auto ranks_per_dim = partitioner.ranksPerDimension( 163 | MPI_COMM_WORLD, global_num_cell ); 164 | std::vector cart_dims( 3 ); 165 | std::vector cart_period( 3 ); 166 | std::vector cart_rank( 3 ); 167 | MPI_Cart_get( 168 | grid_comm, 3, cart_dims.data(), cart_period.data(), cart_rank.data() ); 169 | for ( int d = 0; d < 3; ++d ) 170 | { 171 | EXPECT_EQ( cart_period[d], is_dim_periodic[d] ); 172 | EXPECT_EQ( global_grid->dimBlockId(d), cart_rank[d] ); 173 | EXPECT_EQ( global_grid->dimNumBlock(d), ranks_per_dim[d] ); 174 | } 175 | 176 | for ( int d = 0; d < 3; ++d ) 177 | { 178 | std::vector dim_cells_per_rank( global_grid->dimNumBlock(d), 0 ); 179 | dim_cells_per_rank[ global_grid->dimBlockId(d) ] = 180 | global_grid->ownedNumCell(d); 181 | MPI_Allreduce( MPI_IN_PLACE, dim_cells_per_rank.data(), 182 | dim_cells_per_rank.size(), MPI_INT, MPI_MAX, 183 | MPI_COMM_WORLD ); 184 | int dim_offset = 0; 185 | for ( int n = 0; n < global_grid->dimBlockId(d); ++n ) 186 | dim_offset += dim_cells_per_rank[n]; 187 | int dim_sum = 0; 188 | for ( int n = 0; n < global_grid->dimNumBlock(d); ++n ) 189 | dim_sum += dim_cells_per_rank[n]; 190 | EXPECT_EQ( global_grid->globalOffset(d), dim_offset ); 191 | EXPECT_EQ( global_grid->globalNumEntity(Cell(),d), dim_sum ); 192 | } 193 | 194 | // Check block ranks 195 | if ( is_dim_periodic[Dim::I] ) 196 | EXPECT_EQ( global_grid->blockRank(-1,0,0), 197 | global_grid->blockRank(global_grid->dimNumBlock(Dim::I)-1,0,0) ); 198 | else 199 | EXPECT_EQ( global_grid->blockRank(-1,0,0), -1 ); 200 | 201 | if ( is_dim_periodic[Dim::I] ) 202 | EXPECT_EQ( global_grid->blockRank(global_grid->dimNumBlock(Dim::I),0,0), 203 | global_grid->blockRank(0,0,0) ); 204 | else 205 | EXPECT_EQ( global_grid->blockRank(global_grid->dimNumBlock(Dim::I),0,0), -1 ); 206 | 207 | if ( is_dim_periodic[Dim::J] ) 208 | EXPECT_EQ( global_grid->blockRank(0,-1,0), 209 | global_grid->blockRank(0,global_grid->dimNumBlock(Dim::J)-1,0) ); 210 | else 211 | EXPECT_EQ( global_grid->blockRank(0,-1,0), -1 ); 212 | 213 | if ( is_dim_periodic[Dim::J] ) 214 | EXPECT_EQ( global_grid->blockRank(0,global_grid->dimNumBlock(Dim::J),0), 215 | global_grid->blockRank(0,0,0) ); 216 | else 217 | EXPECT_EQ( global_grid->blockRank(0,global_grid->dimNumBlock(Dim::J),0), -1 ); 218 | 219 | if ( is_dim_periodic[Dim::K] ) 220 | EXPECT_EQ( global_grid->blockRank(0,0,-1), 221 | global_grid->blockRank(0,0,global_grid->dimNumBlock(Dim::K)-1) ); 222 | else 223 | EXPECT_EQ( global_grid->blockRank(0,0,-1), -1 ); 224 | 225 | if ( is_dim_periodic[Dim::K] ) 226 | EXPECT_EQ( global_grid->blockRank(0,0,global_grid->dimNumBlock(Dim::K)), 227 | global_grid->blockRank(0,0,0) ); 228 | else 229 | EXPECT_EQ( global_grid->blockRank(0,0,global_grid->dimNumBlock(Dim::K)), -1 ); 230 | } 231 | 232 | //---------------------------------------------------------------------------// 233 | // RUN TESTS 234 | //---------------------------------------------------------------------------// 235 | TEST( global_grid, grid_test ) 236 | { 237 | std::array periodic = {true,true,true}; 238 | gridTest( periodic ); 239 | std::array not_periodic = {false,false,false}; 240 | gridTest( not_periodic ); 241 | } 242 | 243 | //---------------------------------------------------------------------------// 244 | 245 | } // end namespace Test 246 | -------------------------------------------------------------------------------- /unit_test/tstGlobalMesh.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | using namespace Cajita; 21 | 22 | namespace Test 23 | { 24 | 25 | //---------------------------------------------------------------------------// 26 | // Test uniform mesh with cubic cells. 27 | void uniformTest1() 28 | { 29 | std::array low_corner = { -1.2, 0.1, 1.1 }; 30 | std::array high_corner = { -0.3, 9.5, 1.3 }; 31 | double cell_size = 0.05; 32 | 33 | auto global_mesh = createUniformGlobalMesh( 34 | low_corner, high_corner, cell_size ); 35 | 36 | for ( int d = 0; d < 3; ++d ) 37 | EXPECT_DOUBLE_EQ( low_corner[d], global_mesh->lowCorner(d) ); 38 | 39 | for ( int d = 0; d < 3; ++d ) 40 | EXPECT_DOUBLE_EQ( high_corner[d], global_mesh->highCorner(d) ); 41 | 42 | for ( int d = 0; d < 3; ++d ) 43 | EXPECT_DOUBLE_EQ( high_corner[d] - low_corner[d], 44 | global_mesh->extent(d) ); 45 | 46 | std::array num_cell = { 18, 188, 4 }; 47 | for ( int d = 0; d < 3; ++d ) 48 | EXPECT_EQ( num_cell[d], global_mesh->globalNumCell(d) ); 49 | 50 | for ( int d = 0; d < 3; ++d ) 51 | EXPECT_DOUBLE_EQ( global_mesh->cellSize(d), cell_size ); 52 | } 53 | 54 | //---------------------------------------------------------------------------// 55 | // Test uniform mesh with number of cells constructor. 56 | void uniformTest2() 57 | { 58 | std::array low_corner = { -1.2, 0.1, 1.1 }; 59 | std::array high_corner = { -0.3, 9.5, 1.3 }; 60 | std::array num_cell = { 18, 188, 4 }; 61 | 62 | auto global_mesh = createUniformGlobalMesh( 63 | low_corner, high_corner, num_cell ); 64 | 65 | for ( int d = 0; d < 3; ++d ) 66 | EXPECT_DOUBLE_EQ( low_corner[d], global_mesh->lowCorner(d) ); 67 | 68 | for ( int d = 0; d < 3; ++d ) 69 | EXPECT_DOUBLE_EQ( high_corner[d], global_mesh->highCorner(d) ); 70 | 71 | for ( int d = 0; d < 3; ++d ) 72 | EXPECT_DOUBLE_EQ( high_corner[d] - low_corner[d], 73 | global_mesh->extent(d) ); 74 | 75 | for ( int d = 0; d < 3; ++d ) 76 | EXPECT_EQ( num_cell[d], global_mesh->globalNumCell(d) ); 77 | 78 | double cell_size = 0.05; 79 | for ( int d = 0; d < 3; ++d ) 80 | EXPECT_DOUBLE_EQ( global_mesh->cellSize(d), cell_size ); 81 | } 82 | 83 | //---------------------------------------------------------------------------// 84 | // test uniform mesh with cells that can have a different size in each 85 | // dimension 86 | void uniformTest3() 87 | { 88 | std::array low_corner = { -1.2, 0.1, 1.1 }; 89 | std::array high_corner = { -0.3, 9.5, 1.3 }; 90 | std::array cell_size = {0.05,0.05,0.05}; 91 | 92 | auto global_mesh = createUniformGlobalMesh( 93 | low_corner, high_corner, cell_size ); 94 | 95 | for ( int d = 0; d < 3; ++d ) 96 | EXPECT_DOUBLE_EQ( low_corner[d], global_mesh->lowCorner(d) ); 97 | 98 | for ( int d = 0; d < 3; ++d ) 99 | EXPECT_DOUBLE_EQ( high_corner[d], global_mesh->highCorner(d) ); 100 | 101 | for ( int d = 0; d < 3; ++d ) 102 | EXPECT_DOUBLE_EQ( high_corner[d] - low_corner[d], 103 | global_mesh->extent(d) ); 104 | 105 | std::array num_cell = { 18, 188, 4 }; 106 | for ( int d = 0; d < 3; ++d ) 107 | EXPECT_EQ( num_cell[d], global_mesh->globalNumCell(d) ); 108 | 109 | for ( int d = 0; d < 3; ++d ) 110 | EXPECT_DOUBLE_EQ( global_mesh->cellSize(d), cell_size[d] ); 111 | } 112 | 113 | //---------------------------------------------------------------------------// 114 | // test a non uniform mesh 115 | void nonUniformTest() 116 | { 117 | std::vector i_edge = { -0.3, 0.4, 1.1 }; 118 | std::vector j_edge = { 3.3, 8.1, 9.5, 12.2 }; 119 | std::vector k_edge = { -1.1, -0.9, 0.4, 8.8, 19.3 }; 120 | 121 | auto global_mesh = createNonUniformGlobalMesh( i_edge, j_edge, k_edge ); 122 | 123 | EXPECT_FLOAT_EQ( i_edge.front(), global_mesh->lowCorner(Dim::I) ); 124 | EXPECT_FLOAT_EQ( j_edge.front(), global_mesh->lowCorner(Dim::J) ); 125 | EXPECT_FLOAT_EQ( k_edge.front(), global_mesh->lowCorner(Dim::K) ); 126 | 127 | EXPECT_FLOAT_EQ( i_edge.back(), global_mesh->highCorner(Dim::I) ); 128 | EXPECT_FLOAT_EQ( j_edge.back(), global_mesh->highCorner(Dim::J) ); 129 | EXPECT_FLOAT_EQ( k_edge.back(), global_mesh->highCorner(Dim::K) ); 130 | 131 | EXPECT_FLOAT_EQ( i_edge.back() - i_edge.front(), global_mesh->extent(Dim::I) ); 132 | EXPECT_FLOAT_EQ( j_edge.back() - j_edge.front(), global_mesh->extent(Dim::J) ); 133 | EXPECT_FLOAT_EQ( k_edge.back() - k_edge.front(), global_mesh->extent(Dim::K) ); 134 | 135 | EXPECT_EQ( 2, global_mesh->globalNumCell(Dim::I) ); 136 | EXPECT_EQ( 3, global_mesh->globalNumCell(Dim::J) ); 137 | EXPECT_EQ( 4, global_mesh->globalNumCell(Dim::K) ); 138 | 139 | const auto& mesh_i = global_mesh->nonUniformEdge( Dim::I ); 140 | int ni = mesh_i.size(); 141 | for ( int i = 0; i < ni; ++i ) 142 | EXPECT_FLOAT_EQ( i_edge[i], mesh_i[i] ); 143 | 144 | const auto& mesh_j = global_mesh->nonUniformEdge( Dim::J ); 145 | int nj = mesh_j.size(); 146 | for ( int j = 0; j < nj; ++j ) 147 | EXPECT_FLOAT_EQ( j_edge[j], mesh_j[j] ); 148 | 149 | const auto& mesh_k = global_mesh->nonUniformEdge( Dim::K ); 150 | int nk = mesh_k.size(); 151 | for ( int k = 0; k < nk; ++k ) 152 | EXPECT_FLOAT_EQ( k_edge[k], mesh_k[k] ); 153 | } 154 | 155 | //---------------------------------------------------------------------------// 156 | // RUN TESTS 157 | //---------------------------------------------------------------------------// 158 | TEST( mesh, uniform_test ) 159 | { 160 | uniformTest1(); 161 | uniformTest2(); 162 | uniformTest3(); 163 | } 164 | 165 | TEST( mesh, non_uniform_test ) 166 | { 167 | nonUniformTest(); 168 | } 169 | 170 | //---------------------------------------------------------------------------// 171 | 172 | } // end namespace Test 173 | -------------------------------------------------------------------------------- /unit_test/tstHalo.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | using namespace Cajita; 30 | 31 | namespace Test 32 | { 33 | //---------------------------------------------------------------------------// 34 | void gatherScatterTest( const ManualPartitioner& partitioner, 35 | const std::array& is_dim_periodic ) 36 | { 37 | // Create the global grid. 38 | double cell_size = 0.23; 39 | std::array global_num_cell = { 32, 23, 41 }; 40 | std::array global_low_corner = { 1.2, 3.3, -2.8 }; 41 | std::array global_high_corner = 42 | { global_low_corner[0] + cell_size * global_num_cell[0], 43 | global_low_corner[1] + cell_size * global_num_cell[1], 44 | global_low_corner[2] + cell_size * global_num_cell[2] }; 45 | auto global_mesh = createUniformGlobalMesh( global_low_corner, 46 | global_high_corner, 47 | global_num_cell ); 48 | 49 | // Create the global grid. 50 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 51 | global_mesh, 52 | is_dim_periodic, 53 | partitioner ); 54 | 55 | // Create an array on the cells. 56 | unsigned array_halo_width = 3; 57 | int dofs_per_cell = 4; 58 | auto cell_layout = 59 | createArrayLayout( global_grid, array_halo_width, dofs_per_cell, Cell() ); 60 | 61 | // Loop over halo sizes up to the size of the array halo width. 62 | for ( unsigned halo_width = 1; halo_width <= array_halo_width; ++halo_width ) 63 | { 64 | // Assign the owned cells a value of 1 and the rest 0. 65 | auto array = createArray( "array", cell_layout ); 66 | ArrayOp::assign( *array, 0.0, Ghost() ); 67 | ArrayOp::assign( *array, 1.0, Own() ); 68 | 69 | // Create a halo. 70 | auto halo = createHalo( *array, FullHaloPattern(), halo_width ); 71 | 72 | // Gather into the ghosts. 73 | halo->gather( *array ); 74 | 75 | // Check the gather. We should get 1 everywhere in the array now where 76 | // there was ghost overlap. Otherwise there will still be 0. 77 | auto owned_space = cell_layout->indexSpace( Own(), Local() ); 78 | auto ghosted_space = cell_layout->indexSpace( Ghost(), Local() ); 79 | auto host_view = Kokkos::create_mirror_view_and_copy( 80 | Kokkos::HostSpace(), array->view() ); 81 | for ( unsigned i = 0; i < ghosted_space.extent(0); ++i ) 82 | for ( unsigned j = 0; j < ghosted_space.extent(1); ++j ) 83 | for ( unsigned k = 0; k < ghosted_space.extent(2); ++k ) 84 | for ( unsigned l = 0; l < ghosted_space.extent(3); ++l ) 85 | if ( i < owned_space.min(Dim::I) - halo_width || 86 | i >= owned_space.max(Dim::I) + halo_width || 87 | j < owned_space.min(Dim::J) - halo_width || 88 | j >= owned_space.max(Dim::J) + halo_width || 89 | k < owned_space.min(Dim::K) - halo_width || 90 | k >= owned_space.max(Dim::K) + halo_width ) 91 | EXPECT_EQ( host_view(i,j,k,l), 0.0 ); 92 | else 93 | EXPECT_EQ( host_view(i,j,k,l), 1.0 ); 94 | 95 | // Scatter from the ghosts back to owned. 96 | halo->scatter( *array ); 97 | 98 | // Check the scatter. The value of the cell should be a function of how 99 | // many neighbors it has. Corner neighbors get 8, edge neighbors get 4, 100 | // face neighbors get 2, and no neighbors remain at 1. 101 | 102 | // This function checks if an index is in the halo of a low neighbor in 103 | // the given dimension 104 | auto in_dim_min_halo = 105 | [=]( const int i, const int dim ){ 106 | if ( is_dim_periodic[dim] || global_grid->dimBlockId(dim) > 0 ) 107 | return i < (owned_space.min(dim) + halo_width); 108 | else 109 | return false; 110 | }; 111 | 112 | // This function checks if an index is in the halo of a high neighbor in 113 | // the given dimension 114 | auto in_dim_max_halo = 115 | [=]( const int i, const int dim ){ 116 | if ( is_dim_periodic[dim] || 117 | global_grid->dimBlockId(dim) < 118 | global_grid->dimNumBlock(dim) - 1 ) 119 | return i >= (owned_space.max(dim) - halo_width); 120 | else 121 | return false; 122 | }; 123 | 124 | // Check results. Use the halo functions to figure out how many neighbor a 125 | // given cell was ghosted to. 126 | Kokkos::deep_copy( host_view, array->view() ); 127 | for ( unsigned i = owned_space.min(0); i < owned_space.max(0); ++i ) 128 | for ( unsigned j = owned_space.min(1); j < owned_space.max(1); ++j ) 129 | for ( unsigned k = owned_space.min(2); k < owned_space.max(2); ++k ) 130 | { 131 | int num_n = 0; 132 | if ( in_dim_min_halo(i,Dim::I) || in_dim_max_halo(i,Dim::I) ) 133 | ++num_n; 134 | if ( in_dim_min_halo(j,Dim::J) || in_dim_max_halo(j,Dim::J) ) 135 | ++num_n; 136 | if ( in_dim_min_halo(k,Dim::K) || in_dim_max_halo(k,Dim::K) ) 137 | ++num_n; 138 | double scatter_val = std::pow( 2.0, num_n ); 139 | for ( unsigned l = 0; l < owned_space.extent(3); ++l ) 140 | EXPECT_EQ( host_view(i,j,k,l), scatter_val ); 141 | } 142 | } 143 | } 144 | 145 | //---------------------------------------------------------------------------// 146 | template 147 | struct TestHaloReduce; 148 | 149 | template<> 150 | struct TestHaloReduce 151 | { 152 | template 153 | static KOKKOS_INLINE_FUNCTION void 154 | check( ViewType view, int neighbor_rank, int comm_rank, 155 | const int i, const int j, const int k, const int l ) 156 | { 157 | if ( neighbor_rank < comm_rank ) 158 | EXPECT_EQ( view(i,j,k,l), neighbor_rank ); 159 | else 160 | EXPECT_EQ( view(i,j,k,l), comm_rank ); 161 | } 162 | 163 | }; 164 | 165 | template<> 166 | struct TestHaloReduce 167 | { 168 | template 169 | static KOKKOS_INLINE_FUNCTION void 170 | check( ViewType view, int neighbor_rank, int comm_rank, 171 | const int i, const int j, const int k, const int l ) 172 | { 173 | if ( neighbor_rank > comm_rank ) 174 | EXPECT_EQ( view(i,j,k,l), neighbor_rank ); 175 | else 176 | EXPECT_EQ( view(i,j,k,l), comm_rank ); 177 | 178 | } 179 | }; 180 | 181 | template<> 182 | struct TestHaloReduce 183 | { 184 | template 185 | static KOKKOS_INLINE_FUNCTION void 186 | check( ViewType view, int neighbor_rank, int, 187 | const int i, const int j, const int k, const int l ) 188 | { 189 | EXPECT_EQ( view(i,j,k,l), neighbor_rank ); 190 | } 191 | }; 192 | 193 | template 194 | void scatterReduceTest( const ReduceFunc& reduce ) 195 | { 196 | // Create the global grid. 197 | double cell_size = 0.23; 198 | std::array global_num_cell = { 32, 23, 41 }; 199 | std::array global_low_corner = { 1.2, 3.3, -2.8 }; 200 | std::array global_high_corner = 201 | { global_low_corner[0] + cell_size * global_num_cell[0], 202 | global_low_corner[1] + cell_size * global_num_cell[1], 203 | global_low_corner[2] + cell_size * global_num_cell[2] }; 204 | auto global_mesh = createUniformGlobalMesh( global_low_corner, 205 | global_high_corner, 206 | global_num_cell ); 207 | 208 | // Create the global grid. 209 | std::array is_dim_periodic = {true,true,true}; 210 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 211 | global_mesh, 212 | is_dim_periodic, 213 | Cajita::UniformDimPartitioner() ); 214 | 215 | // Create an array on the cells. 216 | unsigned array_halo_width = 2; 217 | int dofs_per_cell = 4; 218 | auto cell_layout = 219 | createArrayLayout( global_grid, array_halo_width, dofs_per_cell, Cell() ); 220 | auto array = createArray( "array", cell_layout ); 221 | 222 | // Assign the rank to the array. 223 | int comm_rank; 224 | MPI_Comm_rank( MPI_COMM_WORLD, &comm_rank ); 225 | ArrayOp::assign( *array, comm_rank, Ghost() ); 226 | 227 | // Create a halo pattern - just write to your 8 corner neighbors so we can 228 | // eliminate overlap between neighbors and not need to resolve the 229 | // collision. 230 | HaloPattern pattern; 231 | std::vector> neighbors = 232 | { {-1,-1,-1}, {1,-1,-1}, {-1,1,-1}, {1,1,-1}, 233 | {-1,-1,1}, {1,-1,1}, {-1,1,1}, {1,1,1} }; 234 | pattern.setNeighbors( neighbors ); 235 | 236 | // Create a halo. 237 | auto halo = createHalo( *array, pattern ); 238 | 239 | // Scatter. 240 | halo->scatter( *array, reduce ); 241 | 242 | // Check the reduction. 243 | auto host_array = Kokkos::create_mirror_view_and_copy( 244 | Kokkos::HostSpace(), array->view() ); 245 | for ( const auto& n : neighbors ) 246 | { 247 | auto neighbor_rank = 248 | cell_layout->localGrid()->neighborRank(n[0],n[1],n[2]); 249 | auto shared_space = 250 | cell_layout->localGrid()->sharedIndexSpace( 251 | Cajita::Own(), Cajita::Cell(), n[0], n[1], n[2] ); 252 | for ( int i = shared_space.min(Dim::I); 253 | i < shared_space.max(Dim::I); 254 | ++i ) 255 | for ( int j = shared_space.min(Dim::J); 256 | j < shared_space.max(Dim::J); 257 | ++j ) 258 | for ( int k = shared_space.min(Dim::K); 259 | k < shared_space.max(Dim::K); 260 | ++k ) 261 | for ( int l = 0; l < 4; ++l ) 262 | { 263 | TestHaloReduce::check( 264 | host_array, neighbor_rank, comm_rank, i, j, k, l ); 265 | } 266 | } 267 | } 268 | 269 | //---------------------------------------------------------------------------// 270 | // RUN TESTS 271 | //---------------------------------------------------------------------------// 272 | TEST( TEST_CATEGORY, not_periodic_test ) 273 | { 274 | // Let MPI compute the partitioning for this test. 275 | int comm_size; 276 | MPI_Comm_size( MPI_COMM_WORLD, &comm_size ); 277 | std::array ranks_per_dim = {0,0,0}; 278 | MPI_Dims_create( comm_size, 3, ranks_per_dim.data() ); 279 | ManualPartitioner partitioner( ranks_per_dim ); 280 | 281 | // Boundaries are not periodic. 282 | std::array is_dim_periodic = {false,false,false}; 283 | 284 | // Test with different block configurations to make sure all the 285 | // dimensions get partitioned even at small numbers of ranks. 286 | gatherScatterTest( partitioner, is_dim_periodic ); 287 | std::swap( ranks_per_dim[0], ranks_per_dim[1] ); 288 | partitioner = ManualPartitioner( ranks_per_dim ); 289 | gatherScatterTest( partitioner, is_dim_periodic ); 290 | std::swap( ranks_per_dim[0], ranks_per_dim[2] ); 291 | partitioner = ManualPartitioner( ranks_per_dim ); 292 | gatherScatterTest( partitioner, is_dim_periodic ); 293 | std::swap( ranks_per_dim[1], ranks_per_dim[2] ); 294 | partitioner = ManualPartitioner( ranks_per_dim ); 295 | gatherScatterTest( partitioner, is_dim_periodic ); 296 | std::swap( ranks_per_dim[0], ranks_per_dim[1] ); 297 | partitioner = ManualPartitioner( ranks_per_dim ); 298 | gatherScatterTest( partitioner, is_dim_periodic ); 299 | } 300 | 301 | //---------------------------------------------------------------------------// 302 | TEST( TEST_CATEGORY, periodic_test ) 303 | { 304 | // Let MPI compute the partitioning for this test. 305 | int comm_size; 306 | MPI_Comm_size( MPI_COMM_WORLD, &comm_size ); 307 | std::array ranks_per_dim = {0,0,0}; 308 | MPI_Dims_create( comm_size, 3, ranks_per_dim.data() ); 309 | ManualPartitioner partitioner( ranks_per_dim ); 310 | 311 | // Every boundary is periodic 312 | std::array is_dim_periodic = {true,true,true}; 313 | 314 | // Test with different block configurations to make sure all the 315 | // dimensions get partitioned even at small numbers of ranks. 316 | gatherScatterTest( partitioner, is_dim_periodic ); 317 | std::swap( ranks_per_dim[0], ranks_per_dim[1] ); 318 | partitioner = ManualPartitioner( ranks_per_dim ); 319 | gatherScatterTest( partitioner, is_dim_periodic ); 320 | std::swap( ranks_per_dim[0], ranks_per_dim[2] ); 321 | partitioner = ManualPartitioner( ranks_per_dim ); 322 | gatherScatterTest( partitioner, is_dim_periodic ); 323 | std::swap( ranks_per_dim[1], ranks_per_dim[2] ); 324 | partitioner = ManualPartitioner( ranks_per_dim ); 325 | gatherScatterTest( partitioner, is_dim_periodic ); 326 | std::swap( ranks_per_dim[0], ranks_per_dim[1] ); 327 | partitioner = ManualPartitioner( ranks_per_dim ); 328 | gatherScatterTest( partitioner, is_dim_periodic ); 329 | } 330 | 331 | //---------------------------------------------------------------------------// 332 | TEST( TEST_CATEGORY, scatter_reduce_max_test ) 333 | { 334 | scatterReduceTest( ScatterReduce::Max() ); 335 | } 336 | 337 | //---------------------------------------------------------------------------// 338 | TEST( TEST_CATEGORY, scatter_reduce_min_test ) 339 | { 340 | scatterReduceTest( ScatterReduce::Min() ); 341 | } 342 | 343 | //---------------------------------------------------------------------------// 344 | TEST( TEST_CATEGORY, scatter_reduce_replace_test ) 345 | { 346 | scatterReduceTest( ScatterReduce::Replace() ); 347 | } 348 | 349 | //---------------------------------------------------------------------------// 350 | 351 | } // end namespace Test 352 | -------------------------------------------------------------------------------- /unit_test/tstHypreStructuredSolver.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | using namespace Cajita; 28 | 29 | namespace Test 30 | { 31 | 32 | //---------------------------------------------------------------------------// 33 | void poissonTest( const std::string& solver_type, const std::string& precond_type ) 34 | { 35 | // Create the global grid. 36 | double cell_size = 0.1; 37 | std::array is_dim_periodic = {false,false,false}; 38 | std::array global_low_corner = {-1.0, -2.0, -1.0 }; 39 | std::array global_high_corner = { 1.0, 1.0, 0.5 }; 40 | auto global_mesh = createUniformGlobalMesh( 41 | global_low_corner, global_high_corner, cell_size ); 42 | 43 | // Create the global grid. 44 | UniformDimPartitioner partitioner; 45 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 46 | global_mesh, 47 | is_dim_periodic, 48 | partitioner ); 49 | 50 | // Create a local grid. 51 | auto local_mesh = createLocalGrid( global_grid, 1 ); 52 | auto owned_space = local_mesh->indexSpace(Own(),Cell(),Local()); 53 | 54 | // Create the RHS. 55 | auto vector_layout = createArrayLayout( local_mesh, 1, Cell() ); 56 | auto rhs = createArray( "rhs", vector_layout ); 57 | ArrayOp::assign( *rhs, 1.0, Own() ); 58 | 59 | // Create the LHS. 60 | auto lhs = createArray( "lhs", vector_layout ); 61 | ArrayOp::assign( *lhs, 0.0, Own() ); 62 | 63 | // Create a solver. 64 | auto solver = 65 | createHypreStructuredSolver( solver_type, *vector_layout ); 66 | 67 | // Create a 7-point 3d laplacian stencil. 68 | std::vector > stencil = 69 | { {0,0,0}, {-1,0,0}, {1,0,0}, {0,-1,0}, {0,1,0}, {0,0,-1}, {0,0,1} }; 70 | solver->setMatrixStencil( stencil ); 71 | 72 | // Create the matrix entries. The stencil is defined over cells. 73 | auto matrix_entry_layout = createArrayLayout( local_mesh, 7, Cell() ); 74 | auto matrix_entries = createArray( 75 | "matrix_entries", matrix_entry_layout ); 76 | auto entry_view = matrix_entries->view(); 77 | Kokkos::parallel_for( 78 | "fill_matrix_entries", 79 | createExecutionPolicy( owned_space, TEST_EXECSPACE() ), 80 | KOKKOS_LAMBDA( const int i, const int j, const int k ){ 81 | entry_view(i,j,k,0) = -6.0; 82 | entry_view(i,j,k,1) = 1.0; 83 | entry_view(i,j,k,2) = 1.0; 84 | entry_view(i,j,k,3) = 1.0; 85 | entry_view(i,j,k,4) = 1.0; 86 | entry_view(i,j,k,5) = 1.0; 87 | entry_view(i,j,k,6) = 1.0; 88 | } ); 89 | 90 | solver->setMatrixValues( *matrix_entries ); 91 | 92 | // Set the tolerance. 93 | solver->setTolerance( 1.0e-8 ); 94 | 95 | // Set the maximum iterations. 96 | solver->setMaxIter( 2000 ); 97 | 98 | // Set the print level. 99 | solver->setPrintLevel( 2 ); 100 | 101 | // Create a preconditioner. 102 | if ( "none" != precond_type ) 103 | { 104 | auto preconditioner = 105 | createHypreStructuredSolver( precond_type, *vector_layout, true ); 106 | solver->setPreconditioner( preconditioner ); 107 | } 108 | 109 | // Setup the problem. 110 | solver->setup(); 111 | 112 | // Solve the problem. 113 | solver->solve( *rhs, *lhs ); 114 | 115 | // Create a solver reference for comparison. 116 | auto lhs_ref = createArray( "lhs_ref", vector_layout ); 117 | ArrayOp::assign( *lhs_ref, 0.0, Own() ); 118 | 119 | auto ref_solver = 120 | createReferenceConjugateGradient( *vector_layout ); 121 | ref_solver->setMatrixStencil( stencil ); 122 | const auto& ref_entries = ref_solver->getMatrixValues(); 123 | auto matrix_view = ref_entries.view(); 124 | auto global_space = local_mesh->indexSpace(Ghost(),Cell(),Global()); 125 | Kokkos::parallel_for( 126 | "fill_ref_entries", 127 | createExecutionPolicy( owned_space, TEST_EXECSPACE() ), 128 | KOKKOS_LAMBDA( const int i, const int j, const int k ){ 129 | matrix_view(i,j,k,0) = -6.0; 130 | matrix_view(i,j,k,1) = 131 | ( i + global_space.min(Dim::I) > 0 ) ? 1.0 : 0.0; 132 | matrix_view(i,j,k,2) = 133 | ( i + global_space.min(Dim::I) < global_space.max(Dim::I) - 1 ) ? 1.0 : 0.0; 134 | matrix_view(i,j,k,3) = 135 | ( j + global_space.min(Dim::J) > 0 ) ? 1.0 : 0.0; 136 | matrix_view(i,j,k,4) = 137 | ( j + global_space.min(Dim::J) < global_space.max(Dim::J) - 1 ) ? 1.0 : 0.0; 138 | matrix_view(i,j,k,5) = 139 | ( k + global_space.min(Dim::K) > 0 ) ? 1.0 : 0.0; 140 | matrix_view(i,j,k,6) = 141 | ( k + global_space.min(Dim::K) < global_space.max(Dim::K) - 1 ) ? 1.0 : 0.0; 142 | }); 143 | 144 | std::vector > diag_stencil = { {0,0,0} }; 145 | ref_solver->setPreconditionerStencil( diag_stencil ); 146 | const auto& preconditioner_entries = ref_solver->getPreconditionerValues(); 147 | auto preconditioner_view = preconditioner_entries.view(); 148 | Kokkos::parallel_for( 149 | "fill_preconditioner_entries", 150 | createExecutionPolicy( owned_space, TEST_EXECSPACE() ), 151 | KOKKOS_LAMBDA( const int i, const int j, const int k ){ 152 | preconditioner_view(i,j,k,0) = -1.0 / 6.0; 153 | } ); 154 | 155 | ref_solver->setTolerance( 1.0e-12 ); 156 | ref_solver->setPrintLevel( 1 ); 157 | ref_solver->setup(); 158 | ref_solver->solve( *rhs, *lhs_ref ); 159 | 160 | // Check the results. 161 | double epsilon = 1.0e-3; 162 | auto lhs_host = Kokkos::create_mirror_view_and_copy( 163 | Kokkos::HostSpace(), lhs->view() ); 164 | auto lhs_ref_host = Kokkos::create_mirror_view_and_copy( 165 | Kokkos::HostSpace(), lhs_ref->view() ); 166 | for ( int i = owned_space.min(Dim::I); 167 | i < owned_space.max(Dim::I); 168 | ++i ) 169 | for ( int j = owned_space.min(Dim::J); 170 | j < owned_space.max(Dim::J); 171 | ++j ) 172 | for ( int k = owned_space.min(Dim::K); 173 | k < owned_space.max(Dim::K); 174 | ++k ) 175 | EXPECT_NEAR( lhs_host(i,j,k,0), lhs_ref_host(i,j,k,0), epsilon ); 176 | 177 | // Setup the problem again. We would need to do this if we changed the 178 | // matrix entries. 179 | solver->setup(); 180 | 181 | // Solve the problem again 182 | ArrayOp::assign( *rhs, 2.0, Own() ); 183 | ArrayOp::assign( *lhs, 0.0, Own() ); 184 | solver->solve( *rhs, *lhs ); 185 | 186 | // Compute another reference solution. 187 | ArrayOp::assign( *lhs_ref, 0.0, Own() ); 188 | ref_solver->solve( *rhs, *lhs_ref ); 189 | 190 | // Check the results again 191 | lhs_host = Kokkos::create_mirror_view_and_copy( 192 | Kokkos::HostSpace(), lhs->view() ); 193 | lhs_ref_host = Kokkos::create_mirror_view_and_copy( 194 | Kokkos::HostSpace(), lhs_ref->view() ); 195 | for ( int i = owned_space.min(Dim::I); 196 | i < owned_space.max(Dim::I); 197 | ++i ) 198 | for ( int j = owned_space.min(Dim::J); 199 | j < owned_space.max(Dim::J); 200 | ++j ) 201 | for ( int k = owned_space.min(Dim::K); 202 | k < owned_space.max(Dim::K); 203 | ++k ) 204 | EXPECT_NEAR( lhs_host(i,j,k,0), lhs_ref_host(i,j,k,0), epsilon ); 205 | } 206 | 207 | //---------------------------------------------------------------------------// 208 | // RUN TESTS 209 | //---------------------------------------------------------------------------// 210 | TEST( structured_solver, pcg_none_test ) 211 | { 212 | poissonTest( "PCG", "none" ); 213 | } 214 | 215 | TEST( structured_solver, gmres_none_test ) 216 | { 217 | poissonTest( "GMRES", "none" ); 218 | } 219 | 220 | TEST( structured_solver, bicgstab_none_test ) 221 | { 222 | poissonTest( "BiCGSTAB", "none" ); 223 | } 224 | 225 | TEST( structured_solver, pfmg_none_test ) 226 | { 227 | poissonTest( "PFMG", "none" ); 228 | } 229 | 230 | TEST( structured_solver, pcg_diag_test ) 231 | { 232 | poissonTest( "PCG", "Diagonal" ); 233 | } 234 | 235 | TEST( structured_solver, gmres_diag_test ) 236 | { 237 | poissonTest( "GMRES", "Diagonal" ); 238 | } 239 | 240 | TEST( structured_solver, bicgstab_diag_test ) 241 | { 242 | poissonTest( "BiCGSTAB", "Diagonal" ); 243 | } 244 | 245 | TEST( structured_solver, pcg_jacobi_test ) 246 | { 247 | poissonTest( "PCG", "Jacobi" ); 248 | } 249 | 250 | TEST( structured_solver, gmres_jacobi_test ) 251 | { 252 | poissonTest( "GMRES", "Jacobi" ); 253 | } 254 | 255 | TEST( structured_solver, bicgstab_jacobi_test ) 256 | { 257 | poissonTest( "BiCGSTAB", "Jacobi" ); 258 | } 259 | 260 | //---------------------------------------------------------------------------// 261 | 262 | } // end namespace Test 263 | -------------------------------------------------------------------------------- /unit_test/tstInterpolation.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace Cajita; 33 | 34 | namespace Test 35 | { 36 | 37 | //---------------------------------------------------------------------------// 38 | void interpolationTest() 39 | { 40 | // Create the global mesh. 41 | std::array low_corner = { -1.2, 0.1, 1.1 }; 42 | std::array high_corner = { -0.3, 9.5, 2.3 }; 43 | double cell_size = 0.05; 44 | auto global_mesh = createUniformGlobalMesh( 45 | low_corner, high_corner, cell_size ); 46 | 47 | // Create the global grid. 48 | UniformDimPartitioner partitioner; 49 | std::array is_dim_periodic = {true,true,true}; 50 | auto global_grid = createGlobalGrid( MPI_COMM_WORLD, 51 | global_mesh, 52 | is_dim_periodic, 53 | partitioner ); 54 | 55 | // Create a grid local_grid. 56 | int halo_width = 1; 57 | auto local_grid = createLocalGrid( global_grid, halo_width ); 58 | auto local_mesh = createLocalMesh( *local_grid ); 59 | 60 | // Create a point in the center of every cell. 61 | auto cell_space = 62 | local_grid->indexSpace( Own(), Cell(), Local() ); 63 | int num_point = cell_space.size(); 64 | Kokkos::View points( 65 | Kokkos::ViewAllocateWithoutInitializing("points"), num_point ); 66 | Kokkos::parallel_for( 67 | "fill_points", 68 | createExecutionPolicy(cell_space,TEST_EXECSPACE()), 69 | KOKKOS_LAMBDA( const int i, const int j, const int k ){ 70 | int pi = i - halo_width; 71 | int pj = j - halo_width; 72 | int pk = k - halo_width; 73 | int pid = pi + cell_space.extent(Dim::I) * ( 74 | pj + cell_space.extent(Dim::J) * pk ); 75 | int idx[3] = {i,j,k}; 76 | double x[3]; 77 | local_mesh.coordinates( Cell(), idx, x ); 78 | points(pid,Dim::I) = x[Dim::I]; 79 | points(pid,Dim::J) = x[Dim::J]; 80 | points(pid,Dim::K) = x[Dim::K]; 81 | }); 82 | 83 | // Create a scalar field on the grid. 84 | auto scalar_layout = createArrayLayout( local_grid, 1, Node() ); 85 | auto scalar_grid_field = 86 | createArray( "scalar_grid_field", scalar_layout ); 87 | auto scalar_halo = createHalo( *scalar_grid_field, FullHaloPattern() ); 88 | auto scalar_grid_host = Kokkos::create_mirror_view( scalar_grid_field->view() ); 89 | 90 | // Create a vector field on the grid. 91 | auto vector_layout = createArrayLayout( local_grid, 3, Node() ); 92 | auto vector_grid_field = 93 | createArray( "vector_grid_field", vector_layout ); 94 | auto vector_halo = createHalo( *vector_grid_field, FullHaloPattern() ); 95 | auto vector_grid_host = Kokkos::create_mirror_view( vector_grid_field->view() ); 96 | 97 | // Create a scalar point field. 98 | Kokkos::View 99 | scalar_point_field( Kokkos::ViewAllocateWithoutInitializing( 100 | "scalar_point_field"), num_point ); 101 | auto scalar_point_host = Kokkos::create_mirror_view( scalar_point_field ); 102 | 103 | // Create a vector point field. 104 | Kokkos::View 105 | vector_point_field( Kokkos::ViewAllocateWithoutInitializing( 106 | "vector_point_field"), num_point ); 107 | auto vector_point_host = Kokkos::create_mirror_view( vector_point_field ); 108 | 109 | // Create a tensor point field. 110 | Kokkos::View 111 | tensor_point_field( Kokkos::ViewAllocateWithoutInitializing( 112 | "tensor_point_field"), num_point ); 113 | auto tensor_point_host = Kokkos::create_mirror_view( tensor_point_field ); 114 | 115 | 116 | // P2G 117 | // --- 118 | 119 | Kokkos::deep_copy( scalar_point_field, 3.5 ); 120 | Kokkos::deep_copy( vector_point_field, 3.5 ); 121 | Kokkos::deep_copy( tensor_point_field, 3.5 ); 122 | 123 | // Interpolate a scalar point value to the grid. 124 | ArrayOp::assign( *scalar_grid_field, 0.0, Ghost() ); 125 | auto scalar_p2g = createScalarValueP2G( scalar_point_field, -0.5 ); 126 | p2g( scalar_p2g, points, num_point, Spline<1>(), *scalar_halo, *scalar_grid_field ); 127 | Kokkos::deep_copy( scalar_grid_host, scalar_grid_field->view() ); 128 | for ( int i = cell_space.min(Dim::I); i < cell_space.max(Dim::I); ++i ) 129 | for ( int j = cell_space.min(Dim::J); j < cell_space.max(Dim::J); ++j ) 130 | for ( int k = cell_space.min(Dim::K); k < cell_space.max(Dim::K); ++k ) 131 | EXPECT_FLOAT_EQ( scalar_grid_host(i,j,k,0), -1.75 ); 132 | 133 | // Interpolate a vector point value to the grid. 134 | ArrayOp::assign( *vector_grid_field, 0.0, Ghost() ); 135 | auto vector_p2g = createVectorValueP2G( vector_point_field, -0.5 ); 136 | p2g( vector_p2g, points, num_point, Spline<1>(), *vector_halo, *vector_grid_field ); 137 | Kokkos::deep_copy( vector_grid_host, vector_grid_field->view() ); 138 | for ( int i = cell_space.min(Dim::I); i < cell_space.max(Dim::I); ++i ) 139 | for ( int j = cell_space.min(Dim::J); j < cell_space.max(Dim::J); ++j ) 140 | for ( int k = cell_space.min(Dim::K); k < cell_space.max(Dim::K); ++k ) 141 | for ( int d = 0; d < 3; ++d ) 142 | EXPECT_FLOAT_EQ( vector_grid_host(i,j,k,d), -1.75 ); 143 | 144 | // Interpolate a scalar point gradient value to the grid. 145 | ArrayOp::assign( *vector_grid_field, 0.0, Ghost() ); 146 | auto scalar_grad_p2g = createScalarGradientP2G( scalar_point_field, -0.5 ); 147 | p2g( scalar_grad_p2g, points, num_point, Spline<1>(), *vector_halo, *vector_grid_field ); 148 | Kokkos::deep_copy( vector_grid_host, vector_grid_field->view() ); 149 | for ( int i = cell_space.min(Dim::I); i < cell_space.max(Dim::I); ++i ) 150 | for ( int j = cell_space.min(Dim::J); j < cell_space.max(Dim::J); ++j ) 151 | for ( int k = cell_space.min(Dim::K); k < cell_space.max(Dim::K); ++k ) 152 | for ( int d = 0; d < 3; ++d ) 153 | EXPECT_FLOAT_EQ( vector_grid_host(i,j,k,d) + 1.0, 1.0 ); 154 | 155 | // Interpolate a vector point divergence value to the grid. 156 | ArrayOp::assign( *scalar_grid_field, 0.0, Ghost() ); 157 | auto vector_div_p2g = createVectorDivergenceP2G( vector_point_field, -0.5 ); 158 | p2g( vector_div_p2g, points, num_point, Spline<1>(), *scalar_halo, *scalar_grid_field ); 159 | Kokkos::deep_copy( scalar_grid_host, scalar_grid_field->view() ); 160 | for ( int i = cell_space.min(Dim::I); i < cell_space.max(Dim::I); ++i ) 161 | for ( int j = cell_space.min(Dim::J); j < cell_space.max(Dim::J); ++j ) 162 | for ( int k = cell_space.min(Dim::K); k < cell_space.max(Dim::K); ++k ) 163 | EXPECT_FLOAT_EQ( scalar_grid_host(i,j,k,0) + 1.0, 1.0 ); 164 | 165 | // Interpolate a tensor point divergence value to the grid. 166 | ArrayOp::assign( *vector_grid_field, 0.0, Ghost() ); 167 | auto tensor_div_p2g = createTensorDivergenceP2G( tensor_point_field, -0.5 ); 168 | p2g( tensor_div_p2g, points, num_point, Spline<1>(), *vector_halo, *vector_grid_field ); 169 | Kokkos::deep_copy( vector_grid_host, vector_grid_field->view() ); 170 | for ( int i = cell_space.min(Dim::I); i < cell_space.max(Dim::I); ++i ) 171 | for ( int j = cell_space.min(Dim::J); j < cell_space.max(Dim::J); ++j ) 172 | for ( int k = cell_space.min(Dim::K); k < cell_space.max(Dim::K); ++k ) 173 | for ( int d = 0; d < 3; ++d ) 174 | EXPECT_FLOAT_EQ( vector_grid_host(i,j,k,d) + 1.0, 1.0 ); 175 | 176 | 177 | // G2P 178 | // --- 179 | 180 | ArrayOp::assign( *scalar_grid_field, 3.5, Own() ); 181 | ArrayOp::assign( *vector_grid_field, 3.5, Own() ); 182 | 183 | // Interpolate a scalar grid value to the points. 184 | Kokkos::deep_copy( scalar_point_field, 0.0 ); 185 | auto scalar_value_g2p = createScalarValueG2P( scalar_point_field, -0.5 ); 186 | g2p( *scalar_grid_field, *scalar_halo, points, num_point, Spline<1>(), scalar_value_g2p ); 187 | Kokkos::deep_copy( scalar_point_host, scalar_point_field ); 188 | for ( int p = 0; p < num_point; ++p ) 189 | EXPECT_FLOAT_EQ( scalar_point_host(p), -1.75 ); 190 | 191 | // Interpolate a vector grid value to the points. 192 | Kokkos::deep_copy( vector_point_field, 0.0 ); 193 | auto vector_value_g2p = createVectorValueG2P( vector_point_field, -0.5 ); 194 | g2p( *vector_grid_field, *vector_halo, points, num_point, Spline<1>(), vector_value_g2p ); 195 | Kokkos::deep_copy( vector_point_host, vector_point_field ); 196 | for ( int p = 0; p < num_point; ++p ) 197 | for ( int d = 0; d < 3; ++d ) 198 | EXPECT_FLOAT_EQ( vector_point_host(p,d), -1.75 ); 199 | 200 | // Interpolate a scalar grid gradient to the points. 201 | Kokkos::deep_copy( vector_point_field, 0.0 ); 202 | auto scalar_gradient_g2p = createScalarGradientG2P( vector_point_field, -0.5 ); 203 | g2p( *scalar_grid_field, *scalar_halo, points, num_point, Spline<1>(), scalar_gradient_g2p ); 204 | Kokkos::deep_copy( vector_point_host, vector_point_field ); 205 | for ( int p = 0; p < num_point; ++p ) 206 | for ( int d = 0; d < 3; ++d ) 207 | EXPECT_FLOAT_EQ( vector_point_host(p,d) + 1.0, 1.0 ); 208 | 209 | // Interpolate a vector grid gradient to the points. 210 | Kokkos::deep_copy( tensor_point_field, 0.0 ); 211 | auto vector_gradient_g2p = createVectorGradientG2P( tensor_point_field, -0.5 ); 212 | g2p( *vector_grid_field, *vector_halo, points, num_point, Spline<1>(), vector_gradient_g2p ); 213 | Kokkos::deep_copy( tensor_point_host, tensor_point_field ); 214 | for ( int p = 0; p < num_point; ++p ) 215 | for ( int i = 0; i < 3; ++i ) 216 | for ( int j = 0; j < 3; ++j ) 217 | EXPECT_FLOAT_EQ( tensor_point_host(p,i,j) + 1.0, 1.0 ); 218 | 219 | // Interpolate a vector grid divergence to the points. 220 | Kokkos::deep_copy( scalar_point_field, 0.0 ); 221 | auto vector_div_g2p = createVectorDivergenceG2P( scalar_point_field, -0.5 ); 222 | g2p( *vector_grid_field, *vector_halo, points, num_point, Spline<1>(), vector_div_g2p ); 223 | Kokkos::deep_copy( scalar_point_host, scalar_point_field ); 224 | for ( int p = 0; p < num_point; ++p ) 225 | EXPECT_FLOAT_EQ( scalar_point_host(p) + 1.0, 1.0 ); 226 | } 227 | 228 | //---------------------------------------------------------------------------// 229 | // RUN TESTS 230 | //---------------------------------------------------------------------------// 231 | TEST( interpolation, interpolation_test ) 232 | { 233 | interpolationTest(); 234 | } 235 | 236 | //---------------------------------------------------------------------------// 237 | 238 | } // end namespace Test 239 | -------------------------------------------------------------------------------- /unit_test/tstSplines.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | 18 | using namespace Cajita; 19 | 20 | namespace Test 21 | { 22 | //---------------------------------------------------------------------------// 23 | // TESTS 24 | //---------------------------------------------------------------------------// 25 | TEST( cajita_splines, zero_spline_test ) 26 | { 27 | // Check partition of unity for the quadratic spline. 28 | double xp = -1.4; 29 | double low_x = -3.43; 30 | double dx = 0.27; 31 | double rdx = 1.0 / dx; 32 | double values[1]; 33 | 34 | double x0 = Spline<0>::mapToLogicalGrid( xp, rdx, low_x ); 35 | Spline<0>::value( x0, values ); 36 | double sum = 0.0; 37 | for ( auto x : values ) sum += x; 38 | EXPECT_FLOAT_EQ( sum, 1.0 ); 39 | 40 | xp = 2.1789; 41 | x0 = Spline<0>::mapToLogicalGrid( xp, rdx, low_x ); 42 | Spline<0>::value( x0, values ); 43 | sum = 0.0; 44 | for ( auto x : values ) sum += x; 45 | EXPECT_FLOAT_EQ( sum, 1.0 ); 46 | 47 | xp = low_x + 5 * dx; 48 | x0 = Spline<0>::mapToLogicalGrid( xp, rdx, low_x ); 49 | Spline<0>::value( x0, values ); 50 | sum = 0.0; 51 | for ( auto x : values ) sum += x; 52 | EXPECT_FLOAT_EQ( sum, 1.0 ); 53 | 54 | // Check the stencil by putting a point in the center of a dual cell (on a 55 | // node). 56 | int node_id = 4; 57 | xp = low_x + (node_id + 0.25) * dx; 58 | x0 = Spline<0>::mapToLogicalGrid( xp, rdx, low_x ); 59 | int offsets[1]; 60 | Spline<0>::offsets( offsets ); 61 | EXPECT_EQ( int(x0) + offsets[0], node_id); 62 | 63 | int stencil[1]; 64 | Spline<0>::stencil( x0, stencil ); 65 | EXPECT_EQ( stencil[0], node_id); 66 | 67 | // Check the interpolation of a function. 68 | auto grid_func = [=]( const double x ){ return 4.32*x - 0.31; }; 69 | double field[Spline<0>::num_knot]; 70 | field[0] = grid_func( low_x + node_id * dx ); 71 | Spline<0>::value( x0, values ); 72 | double field_xp = field[0] * values[0]; 73 | EXPECT_FLOAT_EQ( field_xp, field[0] ); 74 | 75 | // Check the derivative of a function. 76 | Spline<0>::gradient( x0, rdx, values ); 77 | double field_grad = field[0] * values[0]; 78 | EXPECT_FLOAT_EQ( field_grad, 0.0 ); 79 | } 80 | 81 | TEST( cajita_splines, linear_spline_test ) 82 | { 83 | // Check partition of unity for the linear spline. 84 | double xp = -1.4; 85 | double low_x = -3.43; 86 | double dx = 0.27; 87 | double rdx = 1.0 / dx; 88 | double values[2]; 89 | 90 | double x0 = Spline<1>::mapToLogicalGrid( xp, rdx, low_x ); 91 | Spline<1>::value( x0, values ); 92 | double sum = 0.0; 93 | for ( auto x : values ) sum += x; 94 | EXPECT_FLOAT_EQ( sum, 1.0 ); 95 | 96 | xp = 2.1789; 97 | x0 = Spline<1>::mapToLogicalGrid( xp, rdx, low_x ); 98 | Spline<1>::value( x0, values ); 99 | sum = 0.0; 100 | for ( auto x : values ) sum += x; 101 | EXPECT_FLOAT_EQ( sum, 1.0 ); 102 | 103 | xp = low_x + 5 * dx; 104 | x0 = Spline<1>::mapToLogicalGrid( xp, rdx, low_x ); 105 | Spline<1>::value( x0, values ); 106 | sum = 0.0; 107 | for ( auto x : values ) sum += x; 108 | EXPECT_FLOAT_EQ( sum, 1.0 ); 109 | 110 | // Check the stencil by putting a point in the center of a primal cell. 111 | int cell_id = 4; 112 | xp = low_x + (cell_id + 0.5) * dx; 113 | x0 = Spline<1>::mapToLogicalGrid( xp, rdx, low_x ); 114 | int offsets[2]; 115 | Spline<1>::offsets( offsets ); 116 | EXPECT_EQ( int(x0) + offsets[0], cell_id ); 117 | EXPECT_EQ( int(x0) + offsets[1], cell_id + 1); 118 | 119 | int stencil[2]; 120 | Spline<1>::stencil( x0, stencil ); 121 | EXPECT_EQ( stencil[0], cell_id ); 122 | EXPECT_EQ( stencil[1], cell_id + 1); 123 | 124 | // Check the interpolation of a function. 125 | auto grid_func = [=]( const double x ){ return 4.32*x - 0.31; }; 126 | double field[Spline<1>::num_knot]; 127 | field[0] = grid_func( low_x + cell_id * dx ); 128 | field[1] = grid_func( low_x + (cell_id + 1) * dx ); 129 | Spline<1>::value( x0, values ); 130 | double field_xp = field[0] * values[0] + field[1] * values[1]; 131 | EXPECT_FLOAT_EQ( field_xp, grid_func(xp) ); 132 | 133 | // Check the derivative of a function. 134 | Spline<1>::gradient( x0, rdx, values ); 135 | double field_grad = field[0] * values[0] + field[1] * values[1]; 136 | auto grid_deriv = [=]( const double ){ return 4.32; }; 137 | EXPECT_FLOAT_EQ( field_grad, grid_deriv(xp) ); 138 | } 139 | 140 | TEST( cajita_splines, quadratic_spline_test ) 141 | { 142 | // Check partition of unity for the quadratic spline. 143 | double xp = -1.4; 144 | double low_x = -3.43; 145 | double dx = 0.27; 146 | double rdx = 1.0 / dx; 147 | double values[3]; 148 | 149 | double x0 = Spline<2>::mapToLogicalGrid( xp, rdx, low_x ); 150 | Spline<2>::value( x0, values ); 151 | double sum = 0.0; 152 | for ( auto x : values ) sum += x; 153 | EXPECT_FLOAT_EQ( sum, 1.0 ); 154 | 155 | xp = 2.1789; 156 | x0 = Spline<2>::mapToLogicalGrid( xp, rdx, low_x ); 157 | Spline<2>::value( x0, values ); 158 | sum = 0.0; 159 | for ( auto x : values ) sum += x; 160 | EXPECT_FLOAT_EQ( sum, 1.0 ); 161 | 162 | xp = low_x + 5 * dx; 163 | x0 = Spline<2>::mapToLogicalGrid( xp, rdx, low_x ); 164 | Spline<2>::value( x0, values ); 165 | sum = 0.0; 166 | for ( auto x : values ) sum += x; 167 | EXPECT_FLOAT_EQ( sum, 1.0 ); 168 | 169 | // Check the stencil by putting a point in the center of a dual cell (on a 170 | // node). 171 | int node_id = 4; 172 | xp = low_x + (node_id + 0.25) * dx; 173 | x0 = Spline<2>::mapToLogicalGrid( xp, rdx, low_x ); 174 | int offsets[3]; 175 | Spline<2>::offsets( offsets ); 176 | EXPECT_EQ( int(x0) + offsets[0], node_id - 1); 177 | EXPECT_EQ( int(x0) + offsets[1], node_id); 178 | EXPECT_EQ( int(x0) + offsets[2], node_id + 1); 179 | 180 | int stencil[3]; 181 | Spline<2>::stencil( x0, stencil ); 182 | EXPECT_EQ( stencil[0], node_id - 1); 183 | EXPECT_EQ( stencil[1], node_id); 184 | EXPECT_EQ( stencil[2], node_id + 1); 185 | 186 | // Check the interpolation of a function. 187 | auto grid_func = [=]( const double x ){ return 4.32*x - 0.31; }; 188 | double field[Spline<2>::num_knot]; 189 | field[0] = grid_func( low_x + (node_id - 1) * dx ); 190 | field[1] = grid_func( low_x + node_id * dx ); 191 | field[2] = grid_func( low_x + (node_id + 1) * dx ); 192 | Spline<2>::value( x0, values ); 193 | double field_xp = field[0] * values[0] + field[1] * values[1] + 194 | field[2] * values[2]; 195 | EXPECT_FLOAT_EQ( field_xp, grid_func(xp) ); 196 | 197 | // Check the derivative of a function. 198 | Spline<2>::gradient( x0, rdx, values ); 199 | double field_grad = field[0] * values[0] + field[1] * values[1] + 200 | field[2] * values[2]; 201 | auto grid_deriv = [=]( const double ){ return 4.32; }; 202 | EXPECT_FLOAT_EQ( field_grad, grid_deriv(xp) ); 203 | } 204 | 205 | TEST( cajita_splines, cubic_spline_test ) 206 | { 207 | // Check partition of unity for the cubic spline. 208 | double xp = -1.4; 209 | double low_x = -3.43; 210 | double dx = 0.27; 211 | double rdx = 1.0 / dx; 212 | double values[4]; 213 | 214 | double x0 = Spline<3>::mapToLogicalGrid( xp, rdx, low_x ); 215 | Spline<3>::value( x0, values ); 216 | double sum = 0.0; 217 | for ( auto x : values ) sum += x; 218 | EXPECT_FLOAT_EQ( sum, 1.0 ); 219 | 220 | xp = 2.1789; 221 | x0 = Spline<3>::mapToLogicalGrid( xp, rdx, low_x ); 222 | Spline<3>::value( x0, values ); 223 | sum = 0.0; 224 | for ( auto x : values ) sum += x; 225 | EXPECT_FLOAT_EQ( sum, 1.0 ); 226 | 227 | xp = low_x + 5 * dx; 228 | x0 = Spline<3>::mapToLogicalGrid( xp, rdx, low_x ); 229 | Spline<3>::value( x0, values ); 230 | sum = 0.0; 231 | for ( auto x : values ) sum += x; 232 | EXPECT_FLOAT_EQ( sum, 1.0 ); 233 | 234 | // Check the stencil by putting a point in the center of a primal cell. 235 | int cell_id = 4; 236 | xp = low_x + (cell_id + 0.75) * dx; 237 | x0 = Spline<3>::mapToLogicalGrid( xp, rdx, low_x ); 238 | int offsets[4]; 239 | Spline<3>::offsets( offsets ); 240 | EXPECT_EQ( int(x0) + offsets[0], cell_id - 1 ); 241 | EXPECT_EQ( int(x0) + offsets[1], cell_id ); 242 | EXPECT_EQ( int(x0) + offsets[2], cell_id + 1 ); 243 | EXPECT_EQ( int(x0) + offsets[3], cell_id + 2 ); 244 | 245 | int stencil[4]; 246 | Spline<3>::stencil( x0, stencil ); 247 | EXPECT_EQ( stencil[0], cell_id - 1 ); 248 | EXPECT_EQ( stencil[1], cell_id ); 249 | EXPECT_EQ( stencil[2], cell_id + 1 ); 250 | EXPECT_EQ( stencil[3], cell_id + 2 ); 251 | 252 | // Check the interpolation of a function. 253 | auto grid_func = [=]( const double x ){ return 4.32*x - 0.31; }; 254 | double field[Spline<3>::num_knot]; 255 | field[0] = grid_func( low_x + (cell_id - 1) * dx ); 256 | field[1] = grid_func( low_x + cell_id * dx ); 257 | field[2] = grid_func( low_x + (cell_id + 1) * dx ); 258 | field[3] = grid_func( low_x + (cell_id + 2) * dx ); 259 | Spline<3>::value( x0, values ); 260 | double field_xp = field[0] * values[0] + field[1] * values[1] + 261 | field[2] * values[2] + field[3] * values[3]; 262 | EXPECT_FLOAT_EQ( field_xp, grid_func(xp) ); 263 | 264 | // Check the derivative of a function. 265 | Spline<3>::gradient( x0, rdx, values ); 266 | double field_grad = field[0] * values[0] + field[1] * values[1] + 267 | field[2] * values[2] + field[3] * values[3]; 268 | auto grid_deriv = [=]( const double ){ return 4.32; }; 269 | EXPECT_FLOAT_EQ( field_grad, grid_deriv(xp) ); 270 | } 271 | 272 | } // end namespace Test 273 | -------------------------------------------------------------------------------- /unit_test/unit_test_main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 2019-2020 by the Cajita authors * 3 | * All rights reserved. * 4 | * * 5 | * This file is part of the Cajita library. Cajita is distributed under a * 6 | * BSD 3-clause license. For the licensing terms see the LICENSE file in * 7 | * the top-level directory. * 8 | * * 9 | * SPDX-License-Identifier: BSD-3-Clause * 10 | ****************************************************************************/ 11 | 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | 18 | int main( int argc, char* argv[] ) 19 | { 20 | MPI_Init( &argc, &argv ); 21 | Kokkos::initialize( argc, argv ); 22 | ::testing::InitGoogleTest( &argc, argv ); 23 | int return_val = RUN_ALL_TESTS(); 24 | Kokkos::finalize(); 25 | MPI_Finalize(); 26 | return return_val; 27 | } 28 | --------------------------------------------------------------------------------